zoukankan      html  css  js  c++  java
  • Java5 Concurrent包中的锁机制

    转自http://www.iteye.com/topic/333669

            JDK1.5以后加入了concurrent包,主要是为了提高多线程的开发效率,其中提供了很多支持并发的集合类,其中包括:ConcurrentHashMap。大家知道HashTable也是支持并发环境的,也就是说多线程安全的,那两者有什么区别呢?

    分析

            其实简单的说是同步机制有区别,具体区别又在那里呢?

            请看HashTable的put方法:

    Java代码  收藏代码
    1. /** 
    2.      * Maps the specified <code>key</code> to the specified 
    3.      * <code>value</code> in this hashtable. Neither the key nor the 
    4.      * value can be <code>null</code>. <p> 
    5.      * 
    6.      * The value can be retrieved by calling the <code>get</code> method 
    7.      * with a key that is equal to the original key. 
    8.      * 
    9.      * @param      key     the hashtable key 
    10.      * @param      value   the value 
    11.      * @return     the previous value of the specified key in this hashtable, 
    12.      *             or <code>null</code> if it did not have one 
    13.      * @exception  NullPointerException  if the key or value is 
    14.      *               <code>null</code> 
    15.      * @see     Object#equals(Object) 
    16.      * @see     #get(Object) 
    17.      */  
    18.     public synchronized V put(K key, V value) {  
    19.     // Make sure the value is not null  
    20.     if (value == null) {  
    21.         throw new NullPointerException();  
    22.     }  
    23.   
    24.     // Makes sure the key is not already in the hashtable.  
    25.     Entry tab[] = table;  
    26.     int hash = key.hashCode();  
    27.     int index = (hash & 0x7FFFFFFF) % tab.length;  
    28.     for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {  
    29.         if ((e.hash == hash) && e.key.equals(key)) {  
    30.         V old = e.value;  
    31.         e.value = value;  
    32.         return old;  
    33.         }  
    34.     }  
    35.   
    36.     modCount++;  
    37.     if (count >= threshold) {  
    38.         // Rehash the table if the threshold is exceeded  
    39.         rehash();  
    40.   
    41.             tab = table;  
    42.             index = (hash & 0x7FFFFFFF) % tab.length;  
    43.     }  
    44.   
    45.     // Creates the new entry.  
    46.     Entry<K,V> e = tab[index];  
    47.     tab[index] = new Entry<K,V>(hash, key, value, e);  
    48.     count++;  
    49.     return null;  
    50.     }  

         代码中使用synchronized函数的方式进行同步,这个类的其他方法也是使用这个机制进行同步的。我们先来大致了解一下synchronized的机制:

         在多线程环境中,Java的对象都会隐式的包含有一个同步队列,其中类会有一个,然后每个类实例也会包含一个。如图:

    sync

    Java代码  收藏代码
    1. class Foo {  
    2.       synchronized void doSomething(); // 把同步的线程放入类实例的同步队列  
    3.       synchronized static void doSomething(); //把同步的线程放入类的同步队列  
    4. }  

     然后我们再来看看ConcurrentHashMap 的put方法:

    Java代码  收藏代码
    1. //ConcurrentHashMap  
    2. public V put(K key, V value) {  
    3.         if (value == null)  
    4.             throw new NullPointerException();  
    5.         int hash = hash(key.hashCode());  
    6.         return segmentFor(hash).put(key, hash, value, false);  
    7.     }  
    8.   
    9. //Segment内部类,继承至ReentrantLock  
    10. V put(K key, int hash, V value, boolean onlyIfAbsent) {  
    11.             lock();  
    12.             try {  
    13.                 int c = count;  
    14.                 if (c++ > threshold) // ensure capacity  
    15.                     rehash();  
    16.                 HashEntry<K,V>[] tab = table;  
    17.                 int index = hash & (tab.length - 1);  
    18.                 HashEntry<K,V> first = tab[index];  
    19.                 HashEntry<K,V> e = first;  
    20.                 while (e != null && (e.hash != hash || !key.equals(e.key)))  
    21.                     e = e.next;  
    22.   
    23.                 V oldValue;  
    24.                 if (e != null) {  
    25.                     oldValue = e.value;  
    26.                     if (!onlyIfAbsent)  
    27.                         e.value = value;  
    28.                 }  
    29.                 else {  
    30.                     oldValue = null;  
    31.                     ++modCount;  
    32.                     tab[index] = new HashEntry<K,V>(key, hash, first, value);  
    33.                     count = c; // write-volatile  
    34.                 }  
    35.                 return oldValue;  
    36.             } finally {  
    37.                 unlock();  
    38.             }  
    39.         }  

    ReentrantLock就是Java Concurrent包提供的锁对象,Lock的使用方法大致如下:

    Java代码  收藏代码
    1. Lock l = ...;  
    2. l.lock();  
    3. try {  
    4.     // access the resource protected by this lock  
    5. finally {  
    6.     l.unlock();  
    7. }  

    为什么使用Lock对象会比使用synchronized有更好的性能呢?我们再来看看ReentrantLock的实现:

    Java代码  收藏代码
    1. public class ReentrantLock implements Lock, java.io.Serializable {  
    2.     private static final long serialVersionUID = 7373984872572414699L;  
    3.     /** Synchronizer providing all implementation mechanics */  
    4.     private final Sync sync;  
    5.   
    6.     /** 
    7.      * Base of synchronization control for this lock. Subclassed 
    8.      * into fair and nonfair versions below. Uses AQS state to 
    9.      * represent the number of holds on the lock. 
    10.      */  
    11.     static abstract class Sync extends AbstractQueuedSynchronizer {  
    12.         private static final long serialVersionUID = -5179523762034025860L;  
    13. ..........  
    14. ...........  

     我们从中看到ReentrantLock对象也维护着一个同步队列,性能差别就在于这个队列的实现上,我们再来看AbstractQueuedSynchronizer的代码:

    Java代码  收藏代码
    1.  /** 
    2.      * Inserts node into queue, initializing if necessary. See picture above. 
    3.      * @param node the node to insert 
    4.      * @return node's predecessor 
    5.      */  
    6.     private Node enq(final Node node) {  
    7.         for (;;) {  
    8.             Node t = tail;  
    9.             if (t == null) { // Must initialize  
    10.                 Node h = new Node(); // Dummy header  
    11.                 h.next = node;  
    12.                 node.prev = h;  
    13.                 if (compareAndSetHead(h)) {  
    14.                     tail = node;  
    15.                     return h;  
    16.                 }  
    17.             }  
    18.             else {  
    19.                 node.prev = t;  
    20.                 if (compareAndSetTail(t, node)) {  
    21.                     t.next = node;  
    22.                     return t;  
    23.                 }  
    24.             }  
    25.         }  
    26.     }  
    27.   
    28. /** 
    29.      * CAS head field. Used only by enq 
    30.      */  
    31.     private final boolean compareAndSetHead(Node update) {  
    32.         return unsafe.compareAndSwapObject(this, headOffset, null, update);//使用compare and swap方式  
    33.     }  
    34.   
    35.     /** 
    36.      * CAS tail field. Used only by enq 
    37.      */  
    38.     private final boolean compareAndSetTail(Node expect, Node update) {  
    39.         return unsafe.compareAndSwapObject(this, tailOffset, expect, update);//使用compare and swap方式  
    40.     }  

    到了Unsafe.java这里就要通过本地代码实现了,下面是kaffe里面的本地代码实现:

    C代码  收藏代码
    1. /** 
    2.  * Helper macro, defining a sun.misc.Unsafe compare and swap function  
    3.  * with a given NAME tail and TYPE of arguments. 
    4.  */  
    5. #define KAFFE_UNSAFE_COMPARE_AND_SWAP(NAME, TYPE)  
    6. JNIEXPORT jboolean JNICALL Java_sun_misc_Unsafe_compareAndSwap ## NAME(JNIEnv* env, jobject unsafe UNUSED, jobject obj, jlong offset, TYPE expect, TYPE update)   
    7. {   
    8.   volatile TYPE * address = getFieldAddress(env, obj, offset);   
    9.   if (sizeof(TYPE) == sizeof(gint))   
    10.     return g_atomic_int_compare_and_exchange((volatile gint *) address, (gint) expect, (gint) update);   
    11.   else if (sizeof(TYPE) == sizeof(gpointer))   
    12.     return g_atomic_pointer_compare_and_exchange((volatile gpointer *) address, (gpointer) expect, (gpointer) update);   
    13.   else   
    14.     if (*address == expect) {   
    15.       *address = update;   
    16.       return JNI_TRUE;   
    17.     }   
    18.     else   
    19.       return JNI_FALSE;   
    20. }   

    再看glib的代码:

    C代码  收藏代码
    1. gboolean g_atomic_int_compare_and_exchange (volatile gint *atomic,   
    2.                    gint           oldval,   
    3.                    gint           newval)  
    4. {  
    5.   gint result;  
    6.    
    7.   __asm__ __volatile__ ("lock; cmpxchgl %2, %1"  
    8.             : "=a" (result), "=m" (*atomic)  
    9.             : "r" (newval), "m" (*atomic), "0" (oldval));   
    10.   
    11.   return result == oldval;  
    12. }  

     AT&T汇编,

    cmpxchg

    一条指令总会是原子性的了吧

    总结

         concurrent包中大量使用了新的锁机制,新的Lock机制最终归结到一个原子性操作上,所以会比使用synchronized关键字有更高的性能。

  • 相关阅读:
    使用 Spring data redis 结合 Spring cache 缓存数据配置
    Spring Web Flow 笔记
    Linux 定时实行一次任务命令
    css js 优化工具
    arch Failed to load module "intel"
    go 冒泡排序
    go (break goto continue)
    VirtualBox,Kernel driver not installed (rc=-1908)
    go运算符
    go iota
  • 原文地址:https://www.cnblogs.com/balaamwe/p/2259107.html
Copyright © 2011-2022 走看看