某某茶叶有限公司欢迎您!
金沙棋牌在线 > 金沙棋牌在线 > Java Synchronize 和 Lock 的区别

Java Synchronize 和 Lock 的区别

时间:2019-12-29 06:39

这篇文章通过实例讨论了:

synchronized和lock用途区别

synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。

1.某个线程在等待一个锁的控制权的这段时间需要中断
lock.lockInterruptibly();// 注意这里,可以响应中断
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
3.具有公平锁功能,每个到来的线程都将排队等候

例子参考:http://blog.csdn.net/natian306/article/details/18504111

两者比较:

特性 Synchronized Lock
内置、关键字
需手动释放锁 不必 必须
重入性 支持 支持
中断锁 不支持 支持
公平锁 不支持 支持
读写锁 不支持 支持

- java.concurrent.Lock创建的垃圾

相关概念解析

  • 可重入锁
    如果锁具备可重入性,则称作为可重入锁。
    假设方法A需要获取锁,方法B也需要获取锁,如果执行方法A会调用方法B,则直接执行,不需要重新获取锁,否则就会一直等待下去。

  • 可中断锁
    就是支持手动终端锁
    lock可通过lockInterruptibly支持中断锁
    synchronized只有一下两种情况才会释放锁:

  • 获取锁的线程执行完了该代码块

  • 线程执行发生异常,JVM会让线程自动释放锁

  • 公平锁
    即尽量以请求锁的顺序来获取锁。
    lock创建的时候支持设置公平锁,默认是非公平锁。
    synchronized就是非公平锁,执行顺序是随机的。

  • 读写锁
    lock的ReentrantReadWriteLock可以通过readLock()获取读锁,通过writeLock()获取写锁。只有不同操作的锁之间是互斥的。
    synchronized 不区分,完全锁

  • 比较Lock和synchronized
  • 如何通过编程方式计算延时
  • Lock和synchronized竞争带来的影响
  • 延迟测试中由于遗漏(co-ordinated omission)可能对结果的影响

lock的简单使用

  • 定义
public interface Lock {
    void lock();//获取锁
    void lockInterruptibly() throws InterruptedException;//获取中断锁
    boolean tryLock();//尝试获取锁
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();//释放锁
    Condition newCondition();//新建一个锁环境
}
  • 基本使用
Lock lock = ...;//定义锁
lock.lock();//获取锁
try{
    //处理任务
}catch(Exception ex){
  //捕获异常
}finally{
    lock.unlock();   //释放锁
}
  • 中断锁
    使用中断锁lockInterruptibly必须能抛出异常,千万别在方法内自行捕获了。
    通常调用interrupt()方法是不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。
    所以方法同过lockInterruptibly在等待获取锁的时候,是可以响应中断的,而synchronized只能一直等待下去。
public void method() throws InterruptedException {
    lock.lockInterruptibly();
    try {  
     //.....
    }
    finally {
        lock.unlock();
    }  
}
  • 尝试获取锁
Lock lock = ...;
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){
     }finally{
         lock.unlock();   //释放锁
     } 
}else {
    //如果不能获取锁,则直接做其他事情
}

回到我最喜欢的一个主题:垃圾的创建与分配。可以从我以前的文章(如:性能优化的首要法则和重视性能优化首要法则:逃逸分析的效果)获取更多关于这个议题的细节。尤其弄懂在性能问题上,为什么分配是如此重要的因素。

几天前,当我诊断一些 JIT 编译期间奇怪的分配问题时,发现 java.util.concurrent.locks.ReentrantLock 的分配有问题,不过这只在竞争条件下出现。(这一点很容易证明,只要运行一个在 Lock 上建立竞争并指定 –verbosegc 参数测试程序(类似下面的程序))。

示例是在有 Lock 竞争时 GC 的输出结果:

[GC (Allocation Failure) 16384K->1400K(62976K), 0.0016854 secs]
[GC (Allocation Failure) 17784K->1072K(62976K), 0.0011939 secs]
[GC (Allocation Failure) 17456K->1040K(62976K), 0.0008452 secs]
[GC (Allocation Failure) 17424K->1104K(62976K), 0.0008338 secs]
[GC (Allocation Failure) 17488K->1056K(61952K), 0.0008799 secs]
[GC (Allocation Failure) 17440K->1024K(61952K), 0.0010529 secs]
[GC (Allocation Failure) 17408K->1161K(61952K), 0.0012381 secs]
[GC (Allocation Failure) 17545K->1097K(61440K), 0.0004592 secs]
[GC (Allocation Failure) 16969K->1129K(61952K), 0.0004500 secs]

[GC (Allocation Failure) 17001K->1129K(61952K), 0.0003857 secs]

我怀疑是否是在垃圾回收时必须对清理 Lock 上分配的空间,在高度竞争的环境下,将会选择一种比内建的 ‘synchronized‘ 更坏的同步策略。

当然,这个问题比其他任何问题都更加学术。如果你确实非常关心延迟,你会发现自己从来不会(或者绝不应该)有这样一种情况会需要这么多的线程锁。不过,请继续跟我一起探究这个问题,因为这个过程和结果都非常有趣。

简史:锁是2004年,在Java 1.5中引入的。由于对简单并发结构的迫切需要,锁以及其他并发工具因此而诞生。在这之前,你不得不通过内建的 synchronized 和 Object 的 wait()、notify() 方法来控制并发。