《Java中Lock和synchronized的比较和应用》
2016-07-30 20:07
411 查看
《Java中Lock和synchronized的比较和应用》
尽管synchronized在语法上已经足够简单了,在JDK 5之前只能借助此实现,但是由于是独占锁,性能却不高,因此JDK 5以后就开始借助于JNI来完成更高级的锁实现。JDK 5中的锁是接口java.util.concurrent.locks.Lock。另外java.util.concurrent.locks.ReadWriteLock提供了一对可供读写并发的锁。我们从java.util.concurrent.locks.Lock的使用,可以查询API文档,这里不再说明。
java.util.concurrent.locks.Lock类,既然是锁,肯定有用于线程获取锁和释放锁的方法存在,这两个方法为:
1、void lock();
函数功能:获取锁。如果锁不可用,由于线程调度目的,将禁用此线程,并且在获得锁之前,该线程将一直处于休眠状态。
2、unlock();
函数功能:释放锁。对应于所有的获取锁,例如lock()、tryLock()、tryLock(xx)、lockInterruptibly()等操作,如果成功的话应该对应着一个unlock(),这样可以避免死锁或者资源浪费。
使用Lock同步来模拟AtomicInteger类
我们知道AtomicInteger类是一个int型原子操作类。下面我们就使用Lock类来模拟实现一个AtomicInteger。在模拟实现之前,我们如果不太了解AtomicInteger,可以先看下这个类的API文档以及AtomicInteger源码实现。
AtomicInteger的所有原子操作都依赖于sun.misc.Unsafe类,Unsafe类中相关操作都是对应于一条与平台有关的处理器CAS指令。
使用锁Lock模拟的AtomicInteger类代码如下:
类代码虽长,但思路相当简单。
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class AtomicIntegerLock { private volatile int value; private Lock lock = new ReentrantLock(); public AtomicIntegerLock(int value) { this.value = value; } public void set(int newValue){ lock.lock(); try{ this.value = newValue; }finally{ lock.unlock(); } } public final int get(){ lock.lock(); try{ return value; }finally{ lock.unlock(); } } public final int getAndSet(int newValue){ lock.lock(); try{ int oldValue = value; value = newValue; return oldValue; }finally{ lock.unlock(); } } public final int getAndAdd(int delta){ lock.lock(); try{ int oldValue = value; value+=delta; return oldValue; }finally{ lock.unlock(); } } public final int addAndGet(int delta){ lock.lock(); try{ value+=delta; return value; }finally{ lock.unlock(); } } public final boolean getAndCompare(int expect,int newValue){ lock.lock(); try{ if(this.value == expect){ value = newValue; return true; } else{ return false; } }finally{ lock.unlock(); } } public final int getAndIncrement(){ lock.lock(); try{ return value++; }finally{ lock.unlock(); } } public final int getAndDecrement(){ lock.lock(); try{ return value--; }finally{ lock.unlock(); } } public final int incrementAndGet(){ lock.lock(); try{ return ++value; }finally{ lock.unlock(); } } public final int decrementAndGet(){ lock.lock(); try{ return --value; }finally{ lock.unlock(); } } public final String toString(){ return Integer.toString(get()); } }
Lock同步和synchronized同步两种锁的性能
下面我们使用synchronized和Lock分别进行同步的性能比较:分别开启10个线程,每个线程计数到1000000,统计两种锁同步所花费的时间public class TestAtomicIntegerLock { private static int synValue = 0; public static void main(String[] args) { int threadNum = 10; int maxValue = 1000000; Thread[] t = new Thread[threadNum]; Long begin = System.nanoTime(); for(int i=0;i<threadNum;i++){ AtomicIntegerLock aIL = new AtomicIntegerLock(0); t[i]=new Thread(new Runnable(){ @Override public void run() { for(int j=0;j<maxValue;j++){ aIL.getAndIncrement(); } } }); } for(int i=0;i<threadNum;i++){ t[i].start(); } //main线程等待前面开启的所有线程结束 for(int i=0;i<threadNum;i++){ try { t[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("使用lock所花费的时间为:"+(System.nanoTime()-begin)); int[] lock = new int[0]; begin = System.nanoTime(); for(int i=0;i<threadNum;i++){ synValue = 0; t[i]=new Thread(new Runnable(){ @Override public void run() { for(int j=0;j<maxValue;j++){ synchronized(lock){ ++synValue; } } } }); } for(int i=0;i<threadNum;i++){ t[i].start(); } //main线程等待前面开启的所有线程结束 for(int i=0;i<threadNum;i++){ try { t[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("使用synchronized所花费的时间为:"+(System.nanoTime()-begin)); } }
结果如下:
使用lock所花费的时间为:489742547 使用synchronized所花费的时间为:1660636784
从时间数字来看,可以说明,使用lock的性能要好。
在《深入理解Java虚拟机》这本书上,作者说了这句话:与其说ReentrantLock性能好,还不如说synchronized还有很大优化的余地。在JDK1.6之后,人们发现synchronized与ReentrantLock的性能基本上是完全持平的(上面测试的JDK是1.8,不知道为什么没有持平)。虚拟机在未来的性能改进中肯定会更加偏向于原生的synchronized,所以还是提倡synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。
无论别人怎么说,测试才是王道。当确实synchroinzed同步时我们的性能瓶颈时,我们可以用ReentrantLock来进行性能的测试,如果确实更优,我们就可以选择用ReetrantLock来进行同步。
用Lock来进行同步计数和使用AtomicInteger类计数的性能比较
纯属好奇,刚好用Lock来模拟了下AtmoicInteger,因此,我也就比较了下这两个类在开启10个线程,每个线程计数到1000000的时间。
代码如下:
import java.util.concurrent.atomic.AtomicInteger; public class TestAtomicIntegerLock2 { private static int synValue = 0; public static void main(String[] args) { int threadNum = 10; int maxValue = 1000000; Thread[] t = new Thread[threadNum]; Long begin = System.nanoTime(); for(int i=0;i<threadNum;i++){ AtomicIntegerLock aIL = new AtomicIntegerLock(0); t[i]=new Thread(new Runnable(){ @Override public void run() { for(int j=0;j<maxValue;j++){ aIL.getAndIncrement(); } } }); } for(int i=0;i<threadNum;i++){ t[i].start(); } //main线程等待前面开启的所有线程结束 for(int i=0;i<threadNum;i++){ try { t[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("使用lock所花费的时间为:"+(System.nanoTime()-begin)); int[] lock = new int[0]; begin = System.nanoTime(); for(int i=0;i<threadNum;i++){ AtomicInteger ai = new AtomicInteger(0); t[i]=new Thread(new Runnable(){ @Override public void run() { for(int j=0;j<maxValue;j++){ ai.incrementAndGet(); } } }); } for(int i=0;i<threadNum;i++){ t[i].start(); } //main线程等待前面开启的所有线程结束 for(int i=0;i<threadNum;i++){ try { t[i].join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("使用原子操作类AtomicInteger所花费的时间为:"+(System.nanoTime()-begin)); } }
运行结果如下:
使用lock所花费的时间为:493427269 使用原子操作类AtomicInteger所花费的时间为:85106267
可想可知,使用CAS指令确实更要快的多。
相关文章推荐
- java并发编程---synchronized和lock两种锁的比较
- java多线程基础---synchronized与ReentrantReadWriteLock的介绍与比较
- java多线程:synchronized和lock比较浅析
- Java中ReentrantLock和synchronized两种锁机制比较
- java多线程基础---synchronized与ReentrantReadWriteLock的介绍和比较
- 【Java】Synchronized和Lock比较浅析
- Java并发18:Lock系列-Lock接口与synchronized关键字的比较
- Java之美[从菜鸟到高手演变]之Synchronized和ReentrantLock锁机制的比较
- Java 8:StampedLock,ReadWriteLock以及synchronized的比较
- Java【多线程知识总结(7)】多线程同步问题-关于synchronized代码块和synchronized方法的应用
- Java synchronized 关键字和Lock的随笔
- Java【多线程知识总结(7)】多线程同步问题-关于synchronized代码块和synchronized方法的应用
- Java:使用synchronized和Lock对象获取对象锁
- 各种同步方法性能比较(synchronized,ReentrantLock,Atomic)
- Java:使用synchronized和Lock对象获取对象锁
- 多线程拨号Java版(探讨synchronized和Lock线程同步)
- synchronized 关键字的java应用
- Java中的ReentrantLock和synchronized两种锁定机制的对比
- Synchronized和java.util.concurrent.locks.Lock的区别
- 比较ReentrantLock和synchronized和信号量Semaphore实现的同步性能