在JDK1.6之前Java内置的synchronized关键字是有操作系统内置的互斥锁实现,而获取互斥锁需要系统调用,系统调用又是个费时的操作,所以JDK1.5中的
synchronized比起并发包中Lock性能要差不少。到了1.6引入了偏向锁、轻量级锁、自适应自旋锁等对synchronized进行了优化,从而让synchronized的性能得到了极大的提高,已经不亚于Lock了。
1. 自旋锁
自旋锁相对是传统上的阻塞锁。阻塞锁在线程不能获取锁的情况下,通过系统调用将线程阻塞。而自旋锁则不同,它不阻塞线程,只是让线程继续持有CPU做一些空操作而已。很明显,自旋锁在单CPU系统下是无效的。如果线程等待锁的时间很短,那么自旋锁无疑具有更好的性能,因为它没有上下文切换,但是如果线程等待锁的时间比较长,那么自旋就会造成CPU资源的浪费。到底选择自旋还是阻塞取决于锁的等待时间,但这又难以考量,所以引入了自适应自旋锁,根据这个锁的统计量来选择自旋的时间,超过这个时间自动进入阻塞。
2. 偏向锁
java中正规的偏向锁貌似是这样,指第一个获取锁的线程,如果这个线程再次请求锁,不需要同步,但是一旦有其他线程尝试申请该锁,不管成功与失败,则该锁都不再是偏向锁。这是种在无竞争环境下对锁的优化,一旦出现竞争,立马失去偏向功能。而在ReentrantLock中,
当一个锁被一个线程持有时,如果这个线程再次申请锁,则可以不需要任何同步操作进可以获取该锁。两者虽有少许差别,但想要到达的目的都差不多。在ReentrantLock的实现:
-
final boolean nonfairTryAcquire(int acquires) {
-
final Thread current = Thread.currentThread();
-
int c = getState();
-
if (c == 0) {
-
if (compareAndSetState(0, acquires)) {
-
setExclusiveOwnerThread(current);
-
return true;
-
}
-
}
-
else if (current == getExclusiveOwnerThread()) {
-
int nextc = c + acquires;
-
if (nextc < 0) // overflow
-
throw new Error("Maximum lock count exceeded");
-
setState(nextc);
-
return true;
-
}
-
return false;
-
}
在这里,当一个已获取锁的线程再次申请锁的时候,很显然没有使用任何同步操作。
3. 轻量锁
轻量级锁是相对于之前的普通互斥锁的,而轻量级锁被引入后,原来的普通锁则成为重量级锁。所以,轻量级锁的本质,避免使用操作系统的互斥调用,仅通过CAS来获取锁。当一个线程通过CAS获取锁后,则这个锁为轻量级锁。如果此时另外一个线程试图获取该锁,则锁升级为重量锁,该线程被阻塞。在ReentrantLock中实现就是如此,但ReentrantLock中的锁并没有什么轻量级锁重量级锁之分,获取一个空闲的锁仅需要CAS,可以认为此时是轻量级锁,获取一个非空闲锁,则线程需要阻塞,此时可以认为该锁为重量级锁。
事实上,ReentrantLock在1.5中比synchronized性能要高不少,在1.6中synchronized虽然经过优化,引入偏向锁,轻量级锁等概念,但性能并没有比ReentrantLock高。但synchronized在功能适用的情况下,仍然是第一选择。
JDK中偏向锁跟轻量级锁适用于带有同步代码但无竞争的程序。
阅读(890) | 评论(0) | 转发(0) |