深刻研究 Java Synchronize 和 Lock 的区别与用法

在分布式开发中,锁是线程控制的重要途径。Java为此也提供了2种锁机制,synchronized和lock。作为Java爱好者,天然少不了对比一下这2种机制,也能从中学到些分布式开发须要注意的地方。
 
咱们先从最简单的入手,逐步分析这2种的区别。
 
1、synchronized和lock的用法区别
 
synchronized:在须要同步的对象中加入此控制,synchronized能够加在方法上,也能够加在特定代码块中,括号中表示须要锁的对象。
 
lock:须要显示指定起始位置和终止位置。通常使用ReentrantLock类作为锁,多个线程中必需要使用一个ReentrantLock类作为对象才能保证锁的生效。且在加锁和解锁处须要经过lock()和unlock()显示指出。因此通常会在finally块中写unlock()以防死锁。
 
用法区别比较简单,这里不赘述了,若是不懂的能够看看Java基本语法。
 
2、synchronized和lock性能区别
 
synchronized是托管给JVM执行的,而lock是java写的控制锁的代码。在Java1.5中,synchronize是性能低效的。由于这是一个重量级操做,须要调用操做接口,致使有可能加锁消耗的系统时间比加锁之外的操做还多。相比之下使用Java提供的Lock对象,性能更高一些。可是到了Java1.6,发生了变化。synchronize在语义上很清晰,能够进行不少优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。致使在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在将来的版本中还有优化余地。
 
说到这里,仍是想提一下这2中机制的具体区别。据我所知,synchronized原始采用的是CPU悲观锁机制,即线程得到的是独占锁。独占锁意味着其余线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引发线程上下文切换,当有不少线程竞争锁的时候,会引发CPU频繁的上下文切换致使效率很低。
 
而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操做,若是由于冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操做(Compare and Swap)。咱们能够进一步研究ReentrantLock的源代码,会发现其中比较重要的得到锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。
 
现代的CPU提供了指令,能够自动更新共享数据,并且可以检测到其余线程的干扰,而 compareAndSet() 就用这些代替了锁定。这个算法称做非阻塞算法,意思是一个线程的失败或者挂起不该该影响其余线程的失败或挂起的算法。
 
我也只是了解到这一步,具体到CPU的算法若是感兴趣的读者还能够在查阅下,若是有更好的解释也能够给我留言,我也学习下。
 
3、synchronized和lock用途区别
 
synchronized原语和ReentrantLock在通常状况下没有什么区别,可是在很是复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。
 
1.某个线程在等待一个锁的控制权的这段时间须要中断
2.须要分开处理一些wait-notify,ReentrantLock里面的Condition应用,可以控制notify哪一个线程
3.具备公平锁功能,每一个到来的线程都将排队等候
 
下面细细道来……
 
先说第一种状况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给咱们带来了很大的灵活性。好比:若是A、B2个线程去竞争锁,A线程获得了锁,B线程等待,可是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断本身,再也不等待这个锁了,转而处理其余事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断本身(或者别的线程中断它),可是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断本身(或者别的线程中断它),ReentrantLock处理了这个中断,而且再也不等待这个锁的到来,彻底放弃。(若是你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵)
 
这里来作个试验,首先搞一个Buffer类,它有读操做和写操做,为了避免读到脏数据,写和读都须要加锁,咱们先用synchronized原语来加锁,以下: java

1 public class Buffer {   
2  
3     private Object lock;   
4  
5     public Buffer() {
6         lock = this;
7     }   
8  
9     public void write() {
10         synchronized (lock) {
11             long startTime = System.currentTimeMillis();
12             System.out.println("开始往这个buff写入数据…");
13             for (;;)// 模拟要处理很长时间
14             {
15                 if (System.currentTimeMillis()
16                         - startTime > Integer.MAX_VALUE)
17                     break;
18             }
19             System.out.println("终于写完了");
20         }
21     }   
22  
23     public void read() {
24         synchronized (lock) {
25             System.out.println("从这个buff读数据");
26         }
27     }
28 }
相关文章
相关标签/搜索