- 浏览: 184117 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
liuweihug:
图说浏览器的缓存原理及缓存方式说明(1) http://www ...
浏览器缓存机制 -
fei33423:
楼主为啥不根据评论改下文章..不然会误导很多人的..
Java5 Concurrent包中的锁机制 -
superscorpio:
根据总结,点击back和forward跟是否缓存无关。这应该是 ...
浏览器缓存机制 -
hzxlb910:
引用详细内容请访问我的新博客地址:http://xiaofen ...
Storm的第一份作业 -
tudopi:
遇到F5和enter刷新效果不一样的问题,看了文章解惑了
浏览器缓存机制
JDK1.5以后加入了concurrent包,主要是为了提高多线程的开发效率,其中提供了很多支持并发的集合类,其中包括:ConcurrentHashMap。大家知道HashTable也是支持并发环境的,也就是说多线程安全的,那两者有什么区别呢?
分析
其实简单的说是同步机制有区别,具体区别又在那里呢?
请看HashTable的put方法:
/** * Maps the specified <code>key</code> to the specified * <code>value</code> in this hashtable. Neither the key nor the * value can be <code>null</code>. <p> * * The value can be retrieved by calling the <code>get</code> method * with a key that is equal to the original key. * * @param key the hashtable key * @param value the value * @return the previous value of the specified key in this hashtable, * or <code>null</code> if it did not have one * @exception NullPointerException if the key or value is * <code>null</code> * @see Object#equals(Object) * @see #get(Object) */ public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { V old = e.value; e.value = value; return old; } } modCount++; if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); tab = table; index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. Entry<K,V> e = tab[index]; tab[index] = new Entry<K,V>(hash, key, value, e); count++; return null; }
代码中使用synchronized函数的方式进行同步,这个类的其他方法也是使用这个机制进行同步的。我们先来大致了解一下synchronized的机制:
在多线程环境中,Java的对象都会隐式的包含有一个同步队列,其中类会有一个,然后每个类实例也会包含一个。如图:
class Foo { synchronized void doSomething(); // 把同步的线程放入类实例的同步队列 synchronized static void doSomething(); //把同步的线程放入类的同步队列 }
然后我们再来看看ConcurrentHashMap 的put方法:
//ConcurrentHashMap public V put(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, false); } //Segment内部类,继承至ReentrantLock V put(K key, int hash, V value, boolean onlyIfAbsent) { lock(); try { int c = count; if (c++ > threshold) // ensure capacity rehash(); HashEntry<K,V>[] tab = table; int index = hash & (tab.length - 1); HashEntry<K,V> first = tab[index]; HashEntry<K,V> e = first; while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; V oldValue; if (e != null) { oldValue = e.value; if (!onlyIfAbsent) e.value = value; } else { oldValue = null; ++modCount; tab[index] = new HashEntry<K,V>(key, hash, first, value); count = c; // write-volatile } return oldValue; } finally { unlock(); } }
ReentrantLock就是Java Concurrent包提供的锁对象,Lock的使用方法大致如下:
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
为什么使用Lock对象会比使用synchronized有更好的性能呢?我们再来看看ReentrantLock的实现:
public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; /** Synchronizer providing all implementation mechanics */ private final Sync sync; /** * Base of synchronization control for this lock. Subclassed * into fair and nonfair versions below. Uses AQS state to * represent the number of holds on the lock. */ static abstract class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; .......... ...........
我们从中看到ReentrantLock对象也维护着一个同步队列,性能差别就在于这个队列的实现上,我们再来看AbstractQueuedSynchronizer的代码:
/** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize Node h = new Node(); // Dummy header h.next = node; node.prev = h; if (compareAndSetHead(h)) { tail = node; return h; } } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } } /** * CAS head field. Used only by enq */ private final boolean compareAndSetHead(Node update) { return unsafe.compareAndSwapObject(this, headOffset, null, update);//使用compare and swap方式 } /** * CAS tail field. Used only by enq */ private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update);//使用compare and swap方式 }
到了Unsafe.java这里就要通过本地代码实现了,下面是kaffe里面的本地代码实现:
/** * Helper macro, defining a sun.misc.Unsafe compare and swap function * with a given NAME tail and TYPE of arguments. */ #define KAFFE_UNSAFE_COMPARE_AND_SWAP(NAME, TYPE) JNIEXPORT jboolean JNICALL Java_sun_misc_Unsafe_compareAndSwap ## NAME(JNIEnv* env, jobject unsafe UNUSED, jobject obj, jlong offset, TYPE expect, TYPE update) { volatile TYPE * address = getFieldAddress(env, obj, offset); if (sizeof(TYPE) == sizeof(gint)) return g_atomic_int_compare_and_exchange((volatile gint *) address, (gint) expect, (gint) update); else if (sizeof(TYPE) == sizeof(gpointer)) return g_atomic_pointer_compare_and_exchange((volatile gpointer *) address, (gpointer) expect, (gpointer) update); else if (*address == expect) { *address = update; return JNI_TRUE; } else return JNI_FALSE; }
再看glib的代码:
gboolean g_atomic_int_compare_and_exchange (volatile gint *atomic, gint oldval, gint newval) { gint result; __asm__ __volatile__ ("lock; cmpxchgl %2, %1" : "=a" (result), "=m" (*atomic) : "r" (newval), "m" (*atomic), "0" (oldval)); return result == oldval; }
AT&T汇编,
一条指令总会是原子性的了吧
总结
concurrent包中大量使用了新的锁机制,新的Lock机制最终归结到一个原子性操作上,所以会比使用synchronized关键字有更高的性能。
参考
评论
19 楼
fei33423
2014-06-14
楼主为啥不根据评论改下文章..不然会误导很多人的..
18 楼
harry
2009-02-23
pior 写道
并发真是很头疼的问题`有时候出的很莫名其妙``````不想面对`但又不得不去面对``
遵寻一些模式,使用一些封装好的库(特别是JDK中自带的一些类库),减少代码量,应该会好很多
17 楼
pior
2009-02-23
并发真是很头疼的
问题`有时候出的很莫名其妙``````
不想面对`但又不得不去面对``
问题`有时候出的很莫名其妙``````
不想面对`但又不得不去面对``
16 楼
llq056
2009-02-23
佩服
15 楼
rmn190
2009-02-20
佩服楼主对问题群追猛大,一直"杀"到的底的魄力!
14 楼
ahuaxuan
2009-02-17
引用
The allowed concurrency among update operations is guided by the optional concurrencyLevel constructor argument (default 16), which is used as a hint for internal sizing. The table is internally partitioned to try to permit the indicated number of concurrent updates without contention. Because placement in hash tables is essentially random, the actual concurrency will vary. Ideally, you should choose a value to accommodate as many threads as will ever concurrently modify the table. Using a significantly higher value than you need can waste space and time, and a significantly lower value can lead to thread contention. But overestimates and underestimates within an order of magnitude do not usually have much noticeable impact. A value of one is appropriate when it is known that only one thread will modify and all others will only read. Also, resizing this or any other kind of hash table is a relatively slow operation, so, when possible, it is a good idea to provide estimates of expected table sizes in
这个是ConcurrentHashMap类注释的一部分,注意这个词"concurrencyLevel"它的默认值是16.
锁分离是并发编程中比较常用技术,但是将锁分离用在hashmap上是有一定难度的,hashmap是数组+链表(桶)实现的,而且随着元素的增多,还会产生rehash操作,这样每把分离锁的作用域也会改变,对并发没有一定见地的人是写不出来的
13 楼
AlwenS
2009-02-17
julyboxer 写道
ConcurrentHashMap的性能优异的原因是把Map分离成多份,这样更新的时候只要锁定其中一份,而不需要全部锁住。就像数据库中只需要锁定其中一行,而不需要锁住全表的道理类似。。
正解!
12 楼
aidown
2009-02-17
你可以这么理解
synchronized是jvm内部实现的;
而Concurrent是用Java语言写出来的(就是用java语言把synchronized在jvm内部实现写出来);依靠cpu的cas实现,感觉像乐观锁;
segments那个只是concurrentHashMap里面的一种优化
上面是仅仅是个人理解,
synchronized是jvm内部实现的;
而Concurrent是用Java语言写出来的(就是用java语言把synchronized在jvm内部实现写出来);依靠cpu的cas实现,感觉像乐观锁;
segments那个只是concurrentHashMap里面的一种优化
上面是仅仅是个人理解,
11 楼
sdh5724
2009-02-17
synchronize写程序很麻烦, 不容易操作 并发包解决了这些问题。
10 楼
beneo
2009-02-17
dennis_zane 写道
harry 写道
dennis_zane 写道
似乎没有证据表明synchronize的内部锁最后转化的不是一个原子性操作?
就我所知,ReentrantLock跟synchronize在jdk1.6上的性能差距已经极小,ReentrantLock更多应用在需要细粒度控制加锁范围和特殊加锁功能(tryLock、超时等)的场景下。ConcurrentHashMap的性能优异更多的原因是采用了分离锁的策略带来的,而非是这个方面的区别。
分离锁的策略是指?
就是二楼所说的策略,ConcurrentHashMap是将内部的数组分成了16份,用16个锁来分别同步,这样并发的put、get可以大大加快,但是对于整个map的独占访问将变的更昂贵(如size())。
曾今在IBM DEV上面看到類似的文章,一個人寫的Java理论与实践專題裡面。
9 楼
harry
2009-02-17
dennis_zane 写道
harry 写道dennis_zane 写道
似乎没有证据表明synchronize的内部锁最后转化的不是一个原子性操作?
就我所知,ReentrantLock跟synchronize在jdk1.6上的性能差距已经极小,ReentrantLock更多应用在需要细粒度控制加锁范围和特殊加锁功能(tryLock、超时等)的场景下。ConcurrentHashMap的性能优异更多的原因是采用了分离锁的策略带来的,而非是这个方面的区别。
分离锁的策略是指?
就是二楼所说的策略,ConcurrentHashMap是将内部的数组分成了16份,用16个锁来分别同步,这样并发的put、get可以大大加快,但是对于整个map的独占访问将变的更昂贵(如size())。
3KU,刚仔细的研究了一下,确实是这样的。
ConcurrentHashMap内部的数组分成若干个Segment,每个Segment持有一个数组,通过segmentFor知道位于那个Segment中
final Segment<K,V> segmentFor(int hash) { return segments[(hash >>> segmentShift) & segmentMask]; }
8 楼
dennis_zane
2009-02-17
harry 写道
dennis_zane 写道
似乎没有证据表明synchronize的内部锁最后转化的不是一个原子性操作?
就我所知,ReentrantLock跟synchronize在jdk1.6上的性能差距已经极小,ReentrantLock更多应用在需要细粒度控制加锁范围和特殊加锁功能(tryLock、超时等)的场景下。ConcurrentHashMap的性能优异更多的原因是采用了分离锁的策略带来的,而非是这个方面的区别。
分离锁的策略是指?
就是二楼所说的策略,ConcurrentHashMap是将内部的数组分成了16份,用16个锁来分别同步,这样并发的put、get可以大大加快,但是对于整个map的独占访问将变的更昂贵(如size())。
7 楼
srdrm
2009-02-17
楼主给我们展现了concurrenthashmap 的不少内幕,感谢。至少我一直以为这个类是 lock-free 的, 原来不是。。。
6 楼
harry
2009-02-17
dennis_zane 写道
似乎没有证据表明synchronize的内部锁最后转化的不是一个原子性操作?
就我所知,ReentrantLock跟synchronize在jdk1.6上的性能差距已经极小,ReentrantLock更多应用在需要细粒度控制加锁范围和特殊加锁功能(tryLock、超时等)的场景下。ConcurrentHashMap的性能优异更多的原因是采用了分离锁的策略带来的,而非是这个方面的区别。
分离锁的策略是指?
5 楼
harry
2009-02-17
julyboxer 写道
ConcurrentHashMap的性能优异的原因是把Map分离成多份,这样更新的时候只要锁定其中一份,而不需要全部锁住。就像数据库中只需要锁定其中一行,而不需要锁住全表的道理类似。。
这个没注意,我再去研究一下,呵呵
4 楼
whaosoft
2009-02-17
学习一下!~!~
3 楼
galaxystar
2009-02-16
典型的 CAS 原语使用场景.
2 楼
julyboxer
2009-02-16
ConcurrentHashMap的性能优异的原因是把Map分离成多份,这样更新的时候只要锁定其中一份,而不需要全部锁住。就像数据库中只需要锁定其中一行,而不需要锁住全表的道理类似。。
1 楼
dennis_zane
2009-02-16
似乎没有证据表明synchronize的内部锁最后转化的不是一个原子性操作?
就我所知,ReentrantLock跟synchronize在jdk1.6上的性能差距已经极小,ReentrantLock更多应用在需要细粒度控制加锁范围和特殊加锁功能(tryLock、超时等)的场景下。ConcurrentHashMap的性能优异更多的原因是采用了分离锁的策略带来的,而非是这个方面的区别。
就我所知,ReentrantLock跟synchronize在jdk1.6上的性能差距已经极小,ReentrantLock更多应用在需要细粒度控制加锁范围和特殊加锁功能(tryLock、超时等)的场景下。ConcurrentHashMap的性能优异更多的原因是采用了分离锁的策略带来的,而非是这个方面的区别。
发表评论
-
hibernate启动时的一个小问题
2010-01-07 21:39 1342测试时报错但测试可正常运行javax.naming.NoIni ... -
JRobin简介
2009-03-20 10:49 1338做过网络管理的人,对于MRTG应该不算陌生 ... -
Java中assertion的使用
2009-03-18 16:06 2303assertion的使用是一个复杂的问题,因为这将 ... -
监控系统实现设计
2009-03-18 14:37 0做了半年的网络监控系统,研究过一些开源的解决方案(如:Open ... -
JVM调优总结
2009-03-03 10:40 1315堆大小设置 JVM 中最大堆大小有三方面限制:相关操作系统 ... -
Java Reference的简单总结
2009-02-18 10:45 1200介绍 J ava 2 平台中引入 java.lang.re ... -
该选择哪个Java Map对象?
2009-01-04 16:48 1262HashMap, Hashtable, LinkedHash ... -
Java编码规范
2008-12-08 11:21 10851 介绍(Introduction) 1.1 为什么要有编码规 ... -
在OSGi中运行Servlet
2008-06-13 10:05 2388这几天在研究OSGi这个东东,稍有成果。 什么是OSGi? 简 ... -
使用一个servlet来分配所有的gwt service
2007-12-19 16:30 2196GWT中默认一个service对应一个servlet,这样会使 ... -
让Tapestry5支持中文
2007-04-17 13:18 2371相信很多使用Tapestry5的人和我一样,碰到中文编码的问题 ... -
Struts开发流程
2007-04-06 14:47 2715开发流程如下:1.收集 ... -
重构原则(Java)
2007-04-06 14:44 25731995,Auer曾在文献[AUER95]中指出: 1.应当根 ... -
使用JACOB控制Powerpoint
2007-04-03 16:52 3222什么是JACOB? JACOB是一个JAVA和COM之间的桥接 ... -
为Java ME程序添加蓝牙文本协议
2007-04-03 16:48 1966这篇文章的副标题是: ... -
使用MIDPLogger进行在机调试
2007-04-03 16:47 1075在进行手机蓝牙远程控制的开发中,需要通过蓝牙控制PC,这个过程 ... -
在测试MIDlet时需要考虑的一些重要方面
2007-04-03 16:45 1177在测试MIDlet时需要考虑的一些重要方面: 即使运行环境 ... -
MIDlet的事件模型
2007-04-03 16:44 1150javax.mircoedition.lcdui包实现了一个同 ... -
Java中实现鼠标模拟与键盘映射
2007-04-03 16:41 2489Java SDK 1.3以后实现了Robot类。此类用于为测试 ... -
学习java要经常看的网站[转]
2007-04-03 16:40 1323http://java.sun.com/developer/b ...
相关推荐
3. **健壮性**:Java设计时注重安全性和错误处理,例如通过强类型检查和异常处理机制。 4. **多线程**:Java内置了对多线程编程的支持,允许开发者创建同时执行的多个线程。 5. **网络编程**:Java提供了丰富的...
在Java中,内置锁是通过synchronized关键字实现的,而显示锁则是通过java.util.concurrent.locks包中的接口和类实现的。显示锁的一个重要接口是Lock,它提供了对锁操作的更细粒度控制,而ReentrantLock是Lock的一个...
"java.util.concurrent包中的线程池和消息队列" java.util.concurrent包中的线程池和消息队列是一种高效的多线程编程技术,主要用于解决处理器单元内多个线程执行的问题。线程池技术可以显著减少处理器单元的闲置...
显式锁是Java 5引入的java.util.concurrent.locks包中的Lock接口及其实现类,如ReentrantLock。与内置锁相比,显式锁提供了更多的控制选项,例如非公平锁、可中断锁、定时锁等。显式锁需要程序员手动获取和释放,这...
`java.util.concurrent.locks`包中提供了比`synchronized`关键字更强大、更灵活的锁实现——`ReentrantLock`。 **2.2.1 `ReentrantLock`特性** - 支持公平锁和非公平锁的选择。 - 提供尝试获取锁的能力,可以指定...
5. **Synchronized与Lock**:`synchronized`关键字提供了内置锁机制,确保同一时间只有一个线程访问共享资源。`concurrent`包中的`ReentrantLock`提供了更细粒度的锁控制,包括公平锁和非公平锁,以及可中断和定时...
5. **并发优化**:如果你有大量的XML数据或者复杂的转换逻辑,可以考虑使用`java.concurrent`包中的工具。例如,可以创建一个ExecutorService,将每个XML文件的转换作为一个单独的任务提交,这样多个XML文件可以并行...
`java.util.concurrent`包中的`AbstractQueuedSynchronizer`框架为Java开发者提供了一个强大的工具箱,使得他们能够高效地实现各种同步器。通过对同步状态的有效管理、阻塞和唤醒机制的优化以及灵活的扩展性设计,...
可重入锁是基于Java.util.concurrent.locks包中的ReentrantLock类,提供了比同步锁更灵活的锁定控制机制。读写锁同样在该包中,即ReadWriteLock,允许多个线程并发读取,但写入时只能一个线程进行,可以提高并发性能...
4. **Java并发工具**:Doug Lea详细介绍了java.util.concurrent包中的各种工具类,如Executor框架、Semaphore信号量、CountDownLatch倒计时器、CyclicBarrier回环栅栏和Future异步计算接口。这些工具极大地简化了...
在Java的`java.util.concurrent.locks`包中,`ReentrantReadWriteLock`类实现了读写锁的功能。这个锁允许多个读取者同时访问资源,但在有写入者时,所有读取者和写入者都会被阻塞,以确保数据的一致性。下面我们将...
使用`java.util.concurrent`包中的高级并发工具,如`Atomic`类和`Concurrent`集合,可以实现非阻塞同步,提高程序性能。 9. **volatile 关键字**: 与`synchronized`不同,`volatile`关键字主要用于确保共享变量...
AQS(AbstractQueuedSynchronizer)是Java.util.concurrent包中同步器的基础框架,它的核心设计思想与实现方法在Doug Lea先生的这篇论文中有详细的介绍。论文详细阐述了AQS框架的原理、设计、实现、应用以及性能等...
关于AbstractQueuedSynchronizer,它是java.util.concurrent包中并发控制的核心组件,为Java并发工具类提供了底层机制支持,例如ReentrantLock、Semaphore、CountDownLatch等,都依赖于这个框架来实现同步。...
Java提供了多种机制来保证线程安全,如`synchronized`关键字、`volatile`变量以及`java.util.concurrent`包中的高级并发工具类。 2. **死锁**:当两个或更多的线程在执行过程中,每个线程都在等待另一个线程释放...
书中强调了线程安全、同步、线程池、并发集合、死锁、活锁与饥饿等核心概念,以及如何使用java.util.concurrent包中的类和接口。例如,`ExecutorService`和`Future`接口,它们为管理和控制并发任务提供了便利;`...
Java.util.concurrent包中的`ConcurrentLinkedQueue`、`BlockingQueue`等数据结构可以作为事件队列,线程间通过这些队列进行异步通信。 总的来说,Java的事件机制和观察者模式为对象间的通信提供了一种解耦的方式,...
内置锁是通过synchronized关键字实现的,而显式锁则是通过java.util.concurrent.locks包中的Lock接口及其实现类来提供的。 一、内置锁(synchronized) 1. 同步方法:在方法声明前加上synchronized关键字,整个...