2013年7月31日星期三

Fine-grained parallel locks high KeyLock

 This post last edited by the icebamboo_moyun on 2013-07-20 18:26:34
a fine-grained locking, in some scenes than the synchronized, ReentrantLock such as a higher degree of parallelism for better performance, interested can look at
explain in particular: http://blog.csdn.net/icebamboo_moyun/article/details/9391915

KeyLock code:
public class KeyLock<K> {
// 保存所有锁定的KEY及其信号量
private final ConcurrentMap<K, Semaphore> map = new ConcurrentHashMap<K, Semaphore>();
// 保存每个线程锁定的KEY及其锁定计数
private final ThreadLocal<Map<K, LockInfo>> local = new ThreadLocal<Map<K, LockInfo>>() {
@Override
protected Map<K, LockInfo> initialValue() {
return new HashMap<K, LockInfo>();
}
};

/**
* 锁定key,其他等待此key的线程将进入等待,直到调用{@link #unlock(K)}
* 使用hashcode和equals来判断key是否相同,因此key必须实现{@link #hashCode()}和
* {@link #equals(Object)}方法
*
* @param key
*/
public void lock(K key) {
if (key == null)
return;
LockInfo info = local.get().get(key);
if (info == null) {
Semaphore current = new Semaphore(1);
current.acquireUninterruptibly();
Semaphore previous = map.put(key, current);
if (previous != null)
previous.acquireUninterruptibly();
local.get().put(key, new LockInfo(current));
} else {
info.lockCount++;
}
}

/**
* 释放key,唤醒其他等待此key的线程
* @param key
*/
public void unlock(K key) {
if (key == null)
return;
LockInfo info = local.get().get(key);
if (info != null && --info.lockCount == 0) {
info.current.release();
map.remove(key, info.current);
local.get().remove(key);
}
}

/**
* 锁定多个key
* 建议在调用此方法前先对keys进行排序,使用相同的锁定顺序,防止死锁发生
* @param keys
*/
public void lock(K[] keys) {
if (keys == null)
return;
for (K key : keys) {
lock(key);
}
}

/**
* 释放多个key
* @param keys
*/
public void unlock(K[] keys) {
if (keys == null)
return;
for (K key : keys) {
unlock(key);
}
}

private static class LockInfo {
private final Semaphore current;
private int lockCount;

private LockInfo(Semaphore current) {
this.current = current;
this.lockCount = 1;
}
}
}

------ Solution ------------------------------------- -------
read your blog do on tests, I have a faster way.

is no transfer function, as follows:
public class Test {

  private final AtomicInteger[] iAccounts;
  public Test(int count, int money) {
    iAccounts = new AtomicInteger[count];
    for (int i = 0; i < count; i++)
      iAccounts[i] = new AtomicInteger(money);
  }

  void transferAtomic(int from, int to, int money) {
    if (iAccounts[from].get() < money)
      return;
    iAccounts[from].getAndAdd(-money);
    try {
      Thread.sleep(2);
    }
    catch (Exception e) {
    }
    iAccounts[to].getAndAdd(money);
  }
//...以下略


Create a TransferRunner, with transferAtomic instead transfer function.

This method is faster than using KeyLock 10 times. Test when removing the phrase sleep, get the code of pure efficiency

difference is:
1. KeyLock each account for two of the lock, and transferAtomic is essentially a time lock an account, that is, it will turn out and transferred as two separate operations: this does not affect the correctness of carrying
2. KeyLock many memory allocation (boxing operation, Semaphore, including lock before Integer {key1, key2}), Hash query, rather than no transferAtomic addition to initializing memory allocation and Hash inquiry and therefore more efficient

In fact, when you need to use KeyLock always better alternative (at least I think have to use KeyLock scene), because it is the nature of the two objects were locked (not at the same time, except in the lock () function rejoined synchronization mechanisms).

------ Solution ------------------------------------ --------
have never used semaphores, modeled on the code you write a class with ordinary Object locked, with a ConcurrentHashMap and double-checked lock to guarantee obtain repeatable read lock lock free:



import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class KeyLocks<L> {
 
  private final Map<L, Object> locks = new ConcurrentHashMap<L, Object>();
 
  public Object getLock(L key) {
   
    Object result = locks.get(key);
    if( result == null ) synchronized (locks) {
     
      result = locks.get(key);
      if( result == null ) {
       
        result = new Object();
        locks.put(key, result);
      }
    }
    return result;
  }
}

class Test {
 
  private final int[] accounts;
  private final KeyLocks<Integer> keyLocks;
 
  Test(int count, int money) {
   
    if( count <= 0 )
      throw new IllegalArgumentException("Invalid number of accounts: " + count);
    if( money < 0 )
      throw new IllegalArgumentException("Invalid initial balance: " + money);
   
    accounts = new int[count];
    for(int i=0; i<accounts.length; i++)
      accounts[i] = money;
   
    keyLocks = new KeyLocks<Integer>();
  }
 
  boolean transfer(int from, int to, int money) {
   
    if( from < 0 || from >= accounts.length )
      throw new IndexOutOfBoundsException("Invalid from account: " + from);
   
    if( to < 0 || to >= accounts.length )
      throw new IndexOutOfBoundsException("Invalid to account: " + to);
   
    if( from == to )
      throw new IllegalArgumentException("Cannot transfer between same account.");
   
    if( money < 0 )
      throw new IllegalArgumentException("Invalid transfer amount: " + money);
   
    synchronized (keyLocks.getLock(from)) {
       
        if( accounts[from] < money )
          return false;
       
        accounts[from] -= money;
    }
    synchronized (keyLocks.getLock(to)) {
     
      accounts[to] += money;
    }
    return true;
  }
}


I do not know each visit ThreadLocal and establish how much overhead Semaphore instance, perhaps negligible, then the efficiency of these two implementations may be similar.
------ For reference only -------------------------------------- -
top!
JDK few?
------ For reference only -------------------------------------- -

1.5 +
------ For reference only ---------------------------- -----------
Oh, this is the realization of their own, not the JAVA comes
------ For reference only ----------- ----------------------------
landlord strong ah my generation catching objects
----- - For reference only ---------------------------------------

------ For reference only --- ------------------------------------

------ For reference only ---------- -----------------------------
forgot to say, if you bring sleep, closer to the actual application scenario, transferAtomic still KeyLock faster than about 20%, mainly due to speak in front of the first point, transferAtomic locked while only one account
------ For reference only -------------- -------------------------

Well, yes, this is indeed something worse performance. This is what I used to do caching tool, which looks like the scene

//用户信息
class UserInfo {
String name;
String phone;
}

interface UserInfoDao {
// 获取用户信息
UserInfo getUserInfo(int userId);

// 更新用户信息
void updateUserInfo(int userId, UserInfo userInfo);
}

class UserInfoDaoImpl implements UserInfoDao {

@Override
public UserInfo getUserInfo(int userId) {
UserInfo info = null;
// 数据库取数据
return info;
}

@Override
public void updateUserInfo(int userId, UserInfo userInfo) {
// 更新数据库
}

}

//带缓存的DAO
class UserInfoDaoCachedImpl implements UserInfoDao {
ConcurrentMap<Integer, UserInfo> cache;
UserInfoDaoImpl innerDao;
KeyLock lock;

@Override
public UserInfo getUserInfo(int userId) {
UserInfo info = cache.get(userId); // 先从缓存取
if (info == null) {
lock.lock(userId);// 缓存无数据则锁定ID
try {
info = innerDao.getUserInfo(userId);// 数据库取
if (info != null) {
cache.put(userId, info); // 更新缓存
}
} finally {
lock.unlock(userId);
}
}
return info;
}

@Override
public void updateUserInfo(int userId, UserInfo userInfo) {
lock.lock(userId);
try {
innerDao.updateUserInfo(userId, userInfo);
cache.put(userId, userInfo);
} finally {
lock.unlock(userId);
}
}
}



------ For reference only ---------------------------------- -----
In fact, I did not understand the role of lock here. Any time you want to ensure the consistency of the cache and the Dao?
The only problem when something like this:
T1 updateUserInfo, T2 getUserInfo, this time T1 DB was not updated when updating the cache, T2 acquired the cache, and DB inconsistent.
However, to solve this problem, as long as the cache is updated when the first updateUserInfo not enough?

may be your code did not paste the full, so do not find the reason for non-
------ For reference only --------------- ------------------------


is to ensure that the data in the cache and database consistency
But the problem is not you said that.
imagine it not a locking scenario:
T1 and T2 simultaneously update id to 0 users' information, T1 to update infoA, T2 to be updated as infoB
T1 first complete innerDao.updateUserInfo (0, infoA);
T2 completed innerDao.updateUserInfo (0, infoB);
At this time the database is stored infoB
then
T2 first complete cache.put (0, infoB);
T1 completed cache.put (0, infoA);
when the cache memory is infoA, cache memory and database stored data is not the same

If the entire updateUserInfo locking, we can guarantee the cache, the database is consistent, there can be only a single thread at a time in the update, the other had to wait, even if they are not updated with a user

but with KeyLock right ID locking, both to ensure data consistency but also improve the degree of parallelism: threads operating on the same ID mutex ID for different threads operating in parallel.
although the locking overhead is large, but when compared with the basic database operations is negligible
------ For reference only --------------- ------------------------


carefully read your example, you are not locked in the case of the first judgment and then transfer the account balance is unsafe
For example there are 500 yuan accounts 0, T1 and T2 be transferred out from the account 0 300 yuan
T1 and T2 simultaneously into the balance of judgment, two threads are met 500> 300, have entered into deducting money transfer processing, when two threads have finished processing their account balance is 0 -100 yuan
------ For reference only --------------------------------------- < br>
  
carefully read your example, you are not locked in the case of the first judgment and then transfer the account balance is unsafe   
For example there are 500 yuan accounts 0, T1 and T2 be transferred out from the account 0 300 yuan   
T1 and T2 simultaneously into the balance of judgment, two threads are met 500> 300, have entered into deducting money transfer processing, when two threads have finished processing their account balance is 0 -100 million  

my test code also ignore this, the lock should be added before the judge
------ For reference only ------------------- --------------------
thing. . This is really wrong. But with a lock array seemingly on the line it
------ For reference only ---------------------------- -----------
indeed reasonable scenarios
------ For reference only ------------------ ---------------------

array lock that you put all the threads are mutually exclusive, and locks almost the entire way.
lock array + sleep () must not be KeyLock high efficiency
------ For reference only ---------------------------------------
I'm not locked array. Or look at the code and intuitive, I think the performance KeyLock is this:
public class KeyLock<T> {
  private Map<T, Lock> iLocksMap;

  private Lock iAllocationLock = new ReentrantLock();

  public KeyLock() {
    iLocksMap = new ConcurrentHashMap<>();
  }

  public void lock(T key) {
    Lock existingLock = iLocksMap.get(key);
    if (existingLock != null) {
      existingLock.lock();
      return;
    }

    try {
      iAllocationLock.lock();
      Lock newLock = iLocksMap.get(key);
      if (newLock == null) {
        newLock = new ReentrantLock();
        iLocksMap.put(key, newLock);
      }
      newLock.lock();
      return;
    }
    finally {
      iAllocationLock.unlock();
    }
  }

  public void unlock(T key) {
    Lock existingLock = iLocksMap.get(key);
    if (existingLock != null) {
      existingLock.unlock();
      return;
    }

    try {
      iAllocationLock.lock();
      existingLock = iLocksMap.get(key);
      if (existingLock != null)
        existingLock.unlock();
    }
    finally {
      iAllocationLock.unlock();
    }
  }
}

------ For reference only ----------------------------------- ----

ah, yes, your implementation to meet the needs of (unlock method where it should be cleaned map try it?)

I'm taking into account that the lock reentrancy and fairness so the code will be complicated point

------ For reference only ---------------------------------- -----
here try to prevent someone from simultaneous access iLocksMap.get (key), and then have got null. Then this two thread must have only one able to create Lock, otherwise there will be more in the same Key Lock on.

not directly try to performance considerations, not every lock or unlock should call iAllocationLock. In fact, this implementation is also very fair, you can also re-entry

------ For reference only ---------------------------------- -----


ah, carefully read your code and found a few minor problems, you may consider not very thorough, honest multithreaded something really nerve-wracking. . . :
1, iLocksMap not been cleaned since key range is not limited, so the map will limit growth
2, the lock is not fair, equitable lock request is the first get a lock first, new ReentrantLock (true) is a fair locks, new ReentrantLock () non-fair. When you use this internal lock control, it must also be non-fair
3, in some cases, a different key is also mutually exclusive, that is, one thread to another thread can lock lock key1 key2. This situation is a bit complicated, combine your KeyLock lines of code to illustrate:

T1 requests lock keyA, execution to line 22, will newLock into the iLocksMap, but not yet performed on line 23
same time T2 lock request keyA, execution to line 13, get existingLock lock, then return
T1 waiting in a line 24
T3 request lock keyB, waiting in a line 18, as iAllocationLock obtained by T1, T1 waiting in line 24
this case the request keyB T3 T1, T2 and the request is not in parallel keyA, T3 T1 must wait for the release of, T2 obtained, T2 release line 18 to get iAllocationLock

------ For reference only ---------------------------------- -----
ah, but also discovered a cause deadlock situation, the more serious, and in the same thread KEY lock request two cases, the same combination of lines of code Description:
T1 requests lock keyA, execution to line 22, will newLock into the iLocksMap, but not yet performed on line 23
same time T2 lock request keyA, execution to line 13, get existingLock lock, then return
T1 waiting in a line 24
T2 requested again locked keyB, waiting in a line 18, as iAllocationLock obtained by T1, T1 waiting in line 24
this case
T1 get iAllocationLock waiting newLock
T2 get existingLock waiting iAllocationLock
As newLock and existingLock is the same lock, so the deadlock
------ For reference only --------------------- ------------------


This code is clearly a problem:


    // ...
    if (iAccounts[from].get() < money)
      return;
    iAccounts[from].getAndAdd(-money);
    // ...


------ For reference only ---------------------------------- -----
it really is a problem. .
If we try inside newlock.lock finally put on the line after a seemingly
------ For reference only ------------------- --------------------


agree with what you said, if specific to a particular scene should always have a more optimal way to achieve this.

AtomicInteger problem is that it does not support the "read, check and then decide whether to write" This complex combination of atomic protection.
------ For reference only -------------------------------------- -


measured over overhead, KeyLock ReentrantLock locking overhead is about 3 times, so it is more suitable for processing time-consuming scenarios

your implementation does not take into account the locks map release because key range is uncertain during operation can handle thousands of different key, you can think about the java virtual reference, there may be methods to solve your code locks release
------ For reference only ------------------------------ ---------
do map clean, in addition to track outside threadlocal seemingly no good way. Even adding a dumpLock () function, improper use is still very prone IllegalMonitorStateException. Like this
public class KeyLock<T> {
  private final Map<T, Lock> iLocksMap;

  private final boolean iFair;

  private Lock iAllocationLock;

  public KeyLock(boolean fair) {
    iFair = fair;
    iLocksMap = new ConcurrentHashMap<>();
    iAllocationLock = new ReentrantLock(iFair);
  }

  public void lock(T key) {
    Lock existingLock = iLocksMap.get(key);
    if (existingLock != null) {
      existingLock.lock();
      return;
    }

    Lock newLock = null;
    try {
      iAllocationLock.lock();
      newLock = iLocksMap.get(key);
      if (newLock == null) {
        newLock = new ReentrantLock(iFair);
        iLocksMap.put(key, newLock);
      }
      return;
    }
    finally {
      iAllocationLock.unlock();
      newLock.lock();
    }
  }

  public void unlock(T key) {
    Lock existingLock = iLocksMap.get(key);
    if (existingLock != null) {
      existingLock.unlock();
      return;
    }

    try {
      iAllocationLock.lock();
      existingLock = iLocksMap.get(key);
      if (existingLock != null)
        existingLock.unlock();
    }
    finally {
      iAllocationLock.unlock();
    }
  }

  public void dumpLock(T key) {
    if (iLocksMap.remove(key) != null)
      return;

    try {
      iAllocationLock.lock();
      if (iLocksMap.remove(key) != null)
        return;
    }
    finally {
      iAllocationLock.unlock();
    }

    iLocksMap.remove(key);
  }

------ For reference only ----------------------------------- ----
landlord link this example is also problematic:


    private int[] accounts; 
    private KeyLock<Integer> lock = new KeyLock<Integer>(); 
     
    public boolean transfer(int from, int to, int money) { 
        Integer[] keys = new Integer[] {from, to}; 
        Arrays.sort(keys); //对多个key进行排序,保证锁定顺序防止死锁 
        lock.lock(keys); 
        try { 
            //处理不同的from和to的线程都可进入此同步块 
            if (accounts[from] < money) 
                return false; 
            accounts[from] -= money; 
            accounts[to] += money; 
            return true; 
        } finally { 
            lock.unlock(keys); 
        } 
    } 


(Incidentally, it should be inside the KeyLock KeyLock bar)

Assuming account 3 500
If there are two operations occur simultaneously on two thread:

transfer 300 from 3-5
transfer 300 from 3-6

Since {3,5} and {3,6} are not equal two locks, will lead to two threads may simultaneously access to the account three, like 3 is not locked to protect the same.
------ For reference only -------------------------------------- -
unlock the iAllocationLock step can also be released earlier

public void unlock(T key) {
    Lock existingLock = iLocksMap.get(key);
    if (existingLock != null) {
      existingLock.unlock();
      return;
    }

    try {
      iAllocationLock.lock();
      existingLock = iLocksMap.get(key);
    }
    finally {
      iAllocationLock.unlock();
      if (existingLock != null)
        existingLock.unlock();
    }
  }

------ For reference only ----------------------------------- ----


This code is called in this way

    /**
     * 锁定多个key
     * 建议在调用此方法前先对keys进行排序,使用相同的锁定顺序,防止死锁发生
     * @param keys
     */ 
    public void lock(K[] keys) { 
        if (keys == null) 
            return; 
        for (K key : keys) { 
            lock(key); 
        } 
    }

were requested two key locks instead of Integer [] as requested lock
------ For reference only ---------------- -----------------------

Here he lock (K [] keys) is locked the keys were in the key, not the whole keys as a lock to treat
------ For reference only --------------------------- ------------

Well, if you do not consider iLocksMap cleanup problem is easier to solve, plus clean-up code also becomes complicated
------ For reference only ---------------------------------------

  
measured over overhead, KeyLock ReentrantLock locking overhead is about 3 times, so it is more suitable for processing time-consuming scenarios   
  
your implementation does not take into account the locks map release because key range is uncertain during operation can handle thousands of different key, you can think about the java virtual reference, there may be solution to the release of your code locks  


mechanisms are not familiar with the GC, if a thread using a Object to monitor, count as strong references? If you count it, maybe you can use a similar WeakHashMap means.
------ For reference only -------------------------------------- -

Here he lock (K [] keys) is locked the keys were in the key, not the entire keys treated as a lock  


uh, I was wrong, sorry, with the sort to solve the deadlock problem, thinking too clever, learned
------ For reference only -------- -------------------------------

  
This code is called in this way   
  

    /**
     * 锁定多个key
     * 建议在调用此方法前先对keys进行排序,使用相同的锁定顺序,防止死锁发生
     * @param keys
     */ 
    public void lock(K[] keys) { 
        if (keys == null) 
            return; 
        for (K key : keys) { 
            lock(key); 
        } 
    }
  
were requested two key locks instead of Integer [] as requested lock  


upstairs
------ For reference only -------------------------------- -------

    
measured over overhead, KeyLock ReentrantLock locking overhead is about 3 times, so it is more suitable for processing time-consuming scenarios     
    
your implementation does not take into account the locks map release because key range is uncertain during operation can handle thousands of different key, you can think about the java virtual reference, there may be solution to the release of your code locks    
  
  
  
mechanisms are not familiar with the GC, if a thread using a Object to monitor, count as strong references? If you count it, maybe you can use a similar WeakHashMap means.  


This I was not very clear, if you can use it to achieve asynchronous cleanup, then, coupled with internal lock object is used, then its performance would surely be much better realization of the program of my
------ For reference only ---------------------------------------

he Here lock (K [] keys) are separately locked keys in the key, not the entire keys treated as a lock    
  
  
  
uh, I was wrong, sorry, with the sort to solve the deadlock problem, thinking too clever, learning  


sort to solve the deadlock problem I had reservations, this approach is too easy an inexplicable error (assuming the user did not carefully read the document), and the efficiency is not high, and sometimes even impossible (if I can not sort the Key ?)
------ For reference only ------------------------------------ ---

      
measured over overhead, KeyLock ReentrantLock locking overhead is about 3 times, so it is more suitable for processing time-consuming scenarios       
      
your implementation does not take into account the locks map release because key range is uncertain during operation can handle thousands of different key, you can think about the java virtual reference, there may be solution to the release of your code locks      
    
    
    
mechanisms are not familiar with the GC, if a thread using a Object to monitor, count as strong references? If you count it, maybe you can use a similar WeakHashMap means.    
  
  
  
This I was not very clear, if you can use it to achieve asynchronous cleanup, then, coupled with internal lock object is used, then its performance would surely be a lot better of my implementations  

ReentrantLock
I looked at the source, did not find it passes a reference to the underlying themselves. Underlying reference is NonfairSync or FairSync, although they are ReentrantLock inner class, but they are static, so it does not depend on ReentrantLock instance. I guess even the thread to hold live lock, then the lock itself or can be garbage collected (assuming no any strong references). Try this test to see.

------ For reference only ---------------------------------- -----

Here he lock (K [] keys) is locked the keys were in the key, not the entire keys treated as a lock      
    
    
    
uh, I was wrong, sorry, with the sort to solve the deadlock problem, thinking too clever, learning    
  
  
  
sort to solve the deadlock problem I had reservations, this approach is too easy an inexplicable error (assuming the user did not carefully read the document), and the efficiency is not high, and sometimes even impossible (if I can not sort the Key ?)  

'd certainly not efficient, sort it touches only pass a Comparator on the line, there is no need KEY natural order, as long as all of the same order of processing requests on the line. Fortunately, I do not need real applications simultaneously lock multiple KEY. . .
------ For reference only -------------------------------------- -
Supreme giving birth to ah ~
------ For reference only ----------------------------- ----------
here have to add conditions, Comparator return equal time. equals must return the same, otherwise it will go wrong. Overall a bit too limited sense
------ For reference only ------------------------------- --------



        
        
measured over overhead, KeyLock ReentrantLock locking overhead is about 3 times, so it is more suitable for processing time-consuming scenarios         
        
your implementation does not take into account the locks map release because key range is uncertain during operation can handle thousands of different key, you can think about the java virtual reference, there may be solution to the release of your code locks        
      
      
      
mechanisms are not familiar with the GC, if a thread using a Object to monitor, count as strong references? If you count it, maybe you can use a similar WeakHashMap means.      
    
    
    
This I was not very clear, if you can use it to achieve asynchronous cleanup, then, coupled with internal lock object is used, then its performance would surely be a lot better of my implementations    
  
  
  ReentrantLock
I looked at the source, did not find it passes a reference to the underlying themselves. Underlying reference is NonfairSync or FairSync, although they are ReentrantLock inner class, but they are static, so it does not depend on ReentrantLock instance. I guess even the thread to hold live lock, then the lock itself or can be garbage collected (assuming no any strong references). Try this test to see.   
 


probably a little bit beyond the scope of my abilities, like a long time did not come up with a pilot program ......
just google a bit did not find ready-made answers, turned a bit JLS 7 and oracle of the JVM specification did not find the answer ......
------ For reference only ---------------------------------------
what way multithreading it has not been very high concurrent understand, do not know that there is nothing good article easy to understand!
------ For reference only -------------------------------------- -

          
measured over overhead, KeyLock ReentrantLock locking overhead is about 3 times, so it is more suitable for processing time-consuming scenarios           
          
your implementation does not take into account the locks map release because key range is uncertain during operation can handle thousands of different key, you can think about the java virtual reference, there may be solution to the release of your code locks          
        
        
        
mechanisms are not familiar with the GC, if a thread using a Object to monitor, count as strong references? If you count it, maybe you can use a similar WeakHashMap means.        
      
      
      
This I was not very clear, if you can use it to achieve asynchronous cleanup, then, coupled with internal lock object is used, then its performance would surely be a lot better of my implementations      
    
    
    ReentrantLock
I looked at the source, did not find it passes a reference to the underlying themselves. Underlying reference is NonfairSync or FairSync, although they are ReentrantLock inner class, but they are static, so it does not depend on ReentrantLock instance. I guess even the thread to hold live lock, then the lock itself or can be garbage collected (assuming no any strong references). Try this test to see.     
   
  
  
  
probably a little bit beyond the scope of my abilities, like a long time did not come up with a pilot program ......   
just google a bit did not find ready-made answers, turned a bit JLS 7 and oracle of the JVM specification did not find the answer ......  


Let me talk about experimental program, and then we do together.
build a MyReetrantLock class inherits ReentrantLock, and override the finalize method it outputs a prompt
use WeakReference to establish a lock, and then let a thread lock it.
This thread constantly creating new objects and placed in an ArrayList, forcing the JVM garbage recycling
sometimes call it System.gc ()
Finally release the lock, empty ArrayList, and then repeat the previous steps, sleep for a while.
last output
------ For reference only ------------------------------- --------
test code:
public class Father {

  public static void main(String args[]) {
    new Thread(new Runnable() {
      @Override
      public void run() {
        MyLock strongLock = new MyLock();
        WeakReference<MyLock> weakLock = new WeakReference<MyLock>(strongLock);
        strongLock.lock();
        System.out.println("locked");
        strongLock = null;

        System.out.println("Allocation like crazy");
        List<String> garbage = new ArrayList<String>();
        for (int i = 0; i < 1000000; i++) {
          garbage.add(String.valueOf(i));
        }

        System.out.println("calling gc");
        System.gc();

        try {
          Thread.sleep(1000);
        }
        catch (InterruptedException e) {
          //ignore
        }

        MyLock lock = weakLock.get();
        if (lock == null) {
          System.out.println("damn!");
          return;
        }
        else {
          lock.unlock();
        }

        garbage.clear();

        System.out.println("Allocation like crazy again");
        for (int i = 1000000; i < 2000000; i++) {
          garbage.add(String.valueOf(i));
        }

        System.out.println("calling gc");
        System.gc();
        try {
          Thread.sleep(1000);
        }
        catch (InterruptedException e) {
          // ignore
        }

        if (weakLock.get() == null)
          System.out.println("hooray!!");
      }
    }).start();
  }

  private static class MyLock extends ReentrantLock {
    @Override
    public void finalize() {
      System.out.println("Hi this is me, a lock! But I'm dying, see ya");
    }
  }
}


Test Results:
locked
Allocation like crazy
Hi this is me, a lock! But I'm dying, see ya
calling gc
damn!

prove Lock even if they are thread has also been recovered (it seems I was right before the estimates..)
------ For reference only ------------ ---------------------------

have a solution for any KEY hash value locked
------ For reference only ---------------------------------------
  The reply deleted by an administrator at 2013-07-25 12:27:34

------ For reference only ---------------------------------- -----

have a solution for any hash value locked KEY  


int hash = key.hashCode();
lock.lock(hash);
try {
//do something
} finally {
lock.unlock(hash);
}


------ For reference only ---------------------------------- -----

"Java concurrent programming combat" http://www.amazon.cn/Java% E5% B9% B6% E5% 8F% 91% E7% BC% 96% E7% A8% 8B% E5% AE% 9E% E6% 88% 98 -% E7% 9B% 96% E8% 8C% A8/dp/B0077K9XHW/ref = sr_1_1? s = books & ie = UTF8 & qid = 1374725184 & sr = 1-1 & ; keywords = java +% E5% B9% B6% E5% 8F% 91
This book is good.
------ For reference only -------------------------------------- -

have a solution for any hash value locked KEY    
  
  
  

int hash = key.hashCode();
lock.lock(hash);
try {
//do something
} finally {
lock.unlock(hash);
}
  
 


did not understand. . What solved the problem?
------ For reference only -------------------------------------- -

have a solution for any hash value locked KEY      
    
    
    

int hash = key.hashCode();
lock.lock(hash);
try {
//do something
} finally {
lock.unlock(hash);
}
    
   
  
  
  
did not understand. . What solved the problem?  

key only implement the hashCode method on the line, meaning that with int values ​​for key mapping, and then lock it int value
------ For reference only ----------- ----------------------------
but this does not infinite loop?
------ For reference only -------------------------------------- -

code are not circulating. . .
not the only way to use hash lock would happen is that they are two different key hash value, like two threads simultaneously on these two key when mutually exclusive, as is its recognition KeyLock hash value
with hash to lock also has the benefit of its internal KeyLock MAP efficient because judgment is equal to the Integer, its hashCode and equals methods, high efficiency
------ For reference only - --------------------------------------


In this case, it is not at liberty to release the map in the lock ......
------ For reference only -------------------- -------------------

code are not circulating. . .   
not the only way to use hash lock would happen is that they are two different key hash value, like two threads simultaneously on these two key when mutually exclusive, as is its recognition KeyLock hash value   
with hash to lock also has the benefit of its internal KeyLock MAP efficient because judgment is equal to the Integer, its high efficiency hashCode and equals methods  


o, you mean that the external code to call hashCode, I thought it was inside the KeyLock such processing. .
Whether internal or external certainly not do so, and the reason is that you say the piece hash value conflicts. In this context the efficiency of pointless discussion, because correctness is the first one. Of course, if you say that particular scenario, it is no problem, but even with a trove of TIntegerObjectMap should replace your ConcurrentMap, efficient.

------ For reference only ---------------------------------- -----
ah. . Achieve a daemon thread cleanup on map
public class KeyLock2<T> {
  private final Map<T, Lock> iLocksMap;

  private final boolean iFair;

  private Lock iAllocationLock;

  private Lock iFreeMapLock;

  private ScheduledExecutorService iExecutor;

  public KeyLock2(boolean fair, long collectionRateMilli) {
    iFair = fair;
    iLocksMap = new ConcurrentHashMap<T, Lock>();
    iAllocationLock = new ReentrantLock(iFair);

    iFreeMapLock = new ReentrantLock();
    iExecutor = Executors.newScheduledThreadPool(1);

    iExecutor.scheduleWithFixedDelay(new Runnable() {
      @Override
      public void run() {
        dumpLocks();
      }
    }, 0, collectionRateMilli, TimeUnit.MILLISECONDS);
  }

  public void lock(T key) {
    Lock newLock = null;
    try {
      iAllocationLock.lock();
      newLock = iLocksMap.get(key);
      if (newLock == null) {
        newLock = new ReentrantLock(iFair);
        iLocksMap.put(key, newLock);
      }
      return;
    }
    finally {
      iAllocationLock.unlock();
      if (newLock != null)
        newLock.lock();
    }
  }

  public void unlock(T key) {
    Lock existingLock;
    try {
      iAllocationLock.lock();
      existingLock = iLocksMap.get(key);
    }
    finally {
      iAllocationLock.unlock();
    }

    if (existingLock != null)
      existingLock.unlock();
  }

  public void dumpLocks() {
    iFreeMapLock.lock();
    try {
      for (Iterator<Map.Entry<T, Lock>> entryIter = iLocksMap.entrySet().iterator(); entryIter.hasNext();/*empty*/) {
        Map.Entry<T, Lock> entry = entryIter.next();
        if (entry.getValue().tryLock()) {
          iAllocationLock.lock();
          try {
            entryIter.remove();
          }
          finally {
            iAllocationLock.unlock();
          }
        }
      }
    }
    finally {
      iFreeMapLock.unlock();
    }
  }
}

------ For reference only ----------------------------------- ----

it might as well use my program it directly. . .
Each KeyLock object contains a cleanup thread that the procedures can be more than just a KeyLock object ID of personnel have locked with the lock on the work order ID, there are right. . .
------ For reference only -------------------------------------- -
but they can be used with a KeyLock object ah
------ For reference only ------------------------ ---------------

can not, for example, who has an ID of 1, work orders have an ID of 1, they interfere with each other, without mutual exclusion < br> ------ For reference only ---------------------------------------
class LockKey {
private int iKey;
private int iType;
private LockKey (int key, int type) {
iKey = key;
iType = type;
}

public static getIDLockKey (int key) {
return new LockKey (key, 0);
}

public static getBillLockKey (int key) {
return new LockKey (key, 1);
}

public int getKey () {
}

public boolean equals (Object o) {
if (! o instanceof LockKey)
return false;
return (LockKey) o.iKey == iKey
}
}
------ For reference only --------------------------------- ------
accidentally hit ctrl + enter. . .

class LockKey {
  private int iKey;
  private int iType;
  private LockKey (int key, int type) {
    iKey = key;
    iType = type;
  }

  public static getIDLockKey (int key) {
    return new LockKey(key, 0);
  }

  public static getBillLockKey (int key) {
    return new LockKey(key, 1);
  }

  public int getKey() {
    return iKey;
  }

  public boolean equals (Object o) {
    if (o == null || !o instanceof LockKey)
      return false;
    return ((LockKey) o).iKey == iKey && ((LockKey) o).iType == iType;
  }

  public int hashCode() {
    return iKey * 17 + (iKey << iType) * 17;
  }
}


so it can be shared KeyLock

没有评论:

发表评论