为了比较一下ReentrantLock和synchronized的性能,做了一下性能测试:
参考http://my.oschina.net/leoson/blog/107327
得出结论:
(1)使用Lock的性能比使用synchronized关键字要提高4~5倍;
(2)使用信号量实现同步的速度大约比synchronized要慢10~20%;
(3)使用atomic包的AtomicInter速度是比Lock要快1一个数量级。
ReentrantLock 类
java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。ReentrantLock 类实现了 Lock,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)
reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续)synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。
在查看清单 1 中的代码示例时,可以看到 Lock 和 synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时bomb,当有一天bomb爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放。
Test的源码:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
public abstract class Test {
protected String id;
protected CyclicBarrier barrier;
protected long count;
protected int threadNum;
protected ExecutorService executor;
public Test(String id, CyclicBarrier barrier, long count, int threadNum,
ExecutorService executor) {
this.id = id;
this.barrier = barrier;
this.count = count;
this.threadNum = threadNum;
this.executor = executor;
}
public void startTest() {
long start = System.currentTimeMillis();
for (int j = 0; j < threadNum; j++) {
executor.execute(new Thread() {
// @Override
public void run() {
for (int i = 0; i < count; i++) {
test();
}
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
});
}
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
// 所有线程执行完成之后,才会跑到这一步
long duration = System.currentTimeMillis() - start;
System.out.println(id + " = " + duration);
}
protected abstract void test();
}
测试类ReentreLockTest 源码:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentreLockTest {
private static long COUNT = 1000000;
private static Lock lock = new ReentrantLock();
private static long lockCounter = 0;
private static long syncCounter = 0;
private static long semaCounter = 0;
private static AtomicLong atomicCounter = new AtomicLong(0);
private static Object syncLock = new Object();
private static Semaphore mutex = new Semaphore(1);
public static void testLock(int num, int threadCount) {
}
static long getLock() {
lock.lock();
try {
return lockCounter;
} finally {
lock.unlock();
}
}
static long getSync() {
synchronized (syncLock) {
return syncCounter;
}
}
static long getAtom() {
return atomicCounter.get();
}
static long getSemaphore() throws InterruptedException {
mutex.acquire();
try {
return semaCounter;
} finally {
mutex.release();
}
}
static long getLockInc() {
lock.lock();
try {
return ++lockCounter;
} finally {
lock.unlock();
}
}
static long getSyncInc() {
synchronized (syncLock) {
return ++syncCounter;
}
}
static long getAtomInc() {
return atomicCounter.getAndIncrement();
}
static class SemaTest extends Test {
public SemaTest(String id, CyclicBarrier barrier, long count,
int threadNum, ExecutorService executor) {
super(id, barrier, count, threadNum, executor);
}
@Override
protected void test() {
try {
getSemaphore();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class LockTest extends Test {
public LockTest(String id, CyclicBarrier barrier, long count,
int threadNum, ExecutorService executor) {
super(id, barrier, count, threadNum, executor);
}
@Override
protected void test() {
getLock();
}
}
static class SyncTest extends Test {
public SyncTest(String id, CyclicBarrier barrier, long count,
int threadNum, ExecutorService executor) {
super(id, barrier, count, threadNum, executor);
}
@Override
protected void test() {
getSync();
}
}
static class AtomicTest extends Test {
public AtomicTest(String id, CyclicBarrier barrier, long count,
int threadNum, ExecutorService executor) {
super(id, barrier, count, threadNum, executor);
}
@Override
protected void test() {
getAtom();
}
}
public static void test(String id, long count, int threadNum,
ExecutorService executor) {
final CyclicBarrier barrier = new CyclicBarrier(threadNum + 1,
new Thread() {
@Override
public void run() {
}
});
System.out.println("==============================");
System.out.println("count = " + count + "/t" + "Thread Count = "
+ threadNum);
new LockTest("Lock ", barrier, COUNT, threadNum, executor).startTest();
new SyncTest("Sync ", barrier, COUNT, threadNum, executor).startTest();
new AtomicTest("Atom ", barrier, COUNT, threadNum, executor)
.startTest();
new SemaTest("Sema ", barrier, COUNT, threadNum, executor)
.startTest();
System.out.println("==============================");
}
public static void main(String[] args) {
for (int i = 1; i < 5; i++) {
ExecutorService executor = Executors.newFixedThreadPool(10 * i);
test("", COUNT * i, 10 * i, executor);
}
}
}
结果:
==============================
count = 1000000/tThread Count = 10
Lock = 417
Sync = 1876
Atom = 34
Sema = 3749
==============================
==============================
count = 2000000/tThread Count = 20
Lock = 815
Sync = 3480
Atom = 69
Sema = 7448
==============================
==============================
count = 3000000/tThread Count = 30
Lock = 1234
Sync = 5163
Atom = 93
Sema = 10609
==============================
==============================
count = 4000000/tThread Count = 40
Lock = 1611
Sync = 6934
Atom = 142
Sema = 13243
==============================