Synchronized和Lock的性能区别
测试JDK版本:JDK8
Synchronized锁原理:无锁=>偏向锁=>轻量级锁=>重量级锁
Lock锁原理:底层是JDK的AQS,使用的CAS加等待队列的方式加锁
实验
实验内容
定义一个int变量index,分别使用ReentrantLock 和 synchronized 进行1亿次新增的测试,观察对应的消耗时间
ReentrantLock lock = new ReentrantLock();
int index = 0;
/**
* 使用synchronized自增
*/
public synchronized void addIndexBySync(){
index++;
}
/**
* 使用Lock自增
*/
public void addIndexByLock(){
lock.lock();
index++;
lock.unlock();
}
测试Synchronized在竞争状态下性能表现
/**
* 测试Synchronized在竞争状态下性能表现
*/
@Test
public void testSyncInCompetition() throws InterruptedException {
long currTime = System.currentTimeMillis();
for (int i = 0; i < 1e8; i++) {
addIndexBySync();
}
System.out.println("index:"+index+",synchronized无竞争状态1亿次自增时间为:"+(System.currentTimeMillis()-currTime)+"ms");
currTime = System.currentTimeMillis();
Thread t1 = new Thread(()->{
for (int i = 0; i < 1e8; i++) {
addIndexBySync();
}
});
t1.start();
for (int i = 0; i < 1e8; i++) {
addIndexBySync();
}
t1.join();
System.out.println("index:"+index+",synchronized有竞争状态2亿次自增时间为:"+(System.currentTimeMillis()-currTime)+"ms");
currTime = System.currentTimeMillis();
for (int i = 0; i < 1e8; i++) {
addIndexBySync();
}
System.out.println("index:"+index+",synchronized竞争之后1亿次自增时间为:"+(System.currentTimeMillis()-currTime)+"ms");
currTime = System.currentTimeMillis();
t1 = new Thread(()->{
for (int i = 0; i < 1e8; i++) {
addIndexBySync();
}
});
t1.start();
for (int i = 0; i < 1e8; i++) {
addIndexBySync();
}
t1.join();
System.out.println("index:"+index+",synchronized有竞争状态之后再2亿次竞争状态自增时间为:"+(System.currentTimeMillis()-currTime)+"ms");
}
/**
* 测试Lock在竞争状态下性能表现
*/
@Test
public void testLockInCompetition() throws InterruptedException {
long currTime = System.currentTimeMillis();
for (int i = 0; i < 1e8; i++) {
addIndexByLock();
}
System.out.println("index:"+index+",index:"+index+",lock无竞争状态1亿次自增时间为:"+(System.currentTimeMillis()-currTime)+"ms");
currTime = System.currentTimeMillis();
Thread t1 = new Thread(()->{
for (int i = 0; i < 1e8; i++) {
addIndexByLock();
}
});
t1.start();
for (int i = 0; i < 1e8; i++) {
addIndexByLock();
}
t1.join();
System.out.println("index:"+index+",lock有竞争状态2亿次自增时间为:"+(System.currentTimeMillis()-currTime)+"ms");
currTime = System.currentTimeMillis();
for (int i = 0; i < 1e8; i++) {
addIndexByLock();
}
System.out.println("index:"+index+",lock竞争之后1亿次自增时间为:"+(System.currentTimeMillis()-currTime)+"ms");
currTime = System.currentTimeMillis();
t1 = new Thread(()->{
for (int i = 0; i < 1e8; i++) {
addIndexByLock();
}
});
t1.start();
for (int i = 0; i < 1e8; i++) {
addIndexByLock();
}
t1.join();
System.out.println("index:"+index+",lock有竞争状态之后再2亿次竞争状态自增时间为:"+(System.currentTimeMillis()-currTime)+"ms");
}
实验结果以及理论分析
synchronized在竞争状态下性能表现实验结果:
index:100000000,synchronized无竞争状态1亿次自增时间为:932ms
index:300000000,synchronized有竞争状态2亿次自增时间为:8428ms
index:400000000,synchronized竞争之后1亿次自增时间为:548ms
index:600000000,synchronized有竞争状态之后再2亿次竞争状态自增时间为:8121ms
Process finished with exit code 0
Lock在竞争状态下性能表现
index:100000000,index:100000000,lock无竞争状态1亿次自增时间为:1045ms
index:300000000,lock有竞争状态2亿次自增时间为:5004ms
index:400000000,lock竞争之后1亿次自增时间为:1120ms
index:600000000,lock有竞争状态之后再2亿次竞争状态自增时间为:5959ms
Process finished with exit code 0
结果:
- 在无竞争的状态时,效率:synchronized > lock,多次实验结果稳定
- 在有线程竞争自增时,效率:lock > synchronized ,而且经过多次的实验测定,发现lock的耗时稳定在4~5s,但是synchronized 的耗时,不稳定,从8~12s都有
理论分析:
- 无竞争状态下,synchronized属于无锁状态,由于是JVM层面的指令,优于Lock
- 有竞争状态下,synchronized升级到重量级锁,等待线程会触发操作系统系统调用,从而频繁进行内核态的切换,性能较差,所以比Lock性能表现差,而且由于牵涉到系统调用,会受到操作系统当前状态影响,耗时也会不稳定
- 疑问点:synchronized 不可逆,但是升级为重量级锁之后,为啥之后的无竞争状态反而更快了?