美文网首页
ReentrantLock和Synchronized

ReentrantLock和Synchronized

作者: 凉风拂面秋挽月 | 来源:发表于2020-02-27 12:20 被阅读0次

    相似点:

    两个都是可重入锁,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)。
    功能区别:

    相同点:

    1.它们都是加锁方式同步;
    2.都是重入锁;

    1. 阻塞式的同步;也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善);
      1.不同点(1为Synchronized,2为ReentrantLock):
      原始构成
      (1)它是java语言的关键字,是原生语法层面的互斥,需要jvm实现
      (2)它是JDK 1.5之后提供的API层面的互斥锁类

    2.实现
    (1)通过JVM加锁解锁
    (2)api层面的加锁解锁,需要手动释放锁。

    3.代码编写
    (1)采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用,更安全,
    (2)而ReentrantLock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。需要lock()和unlock()方法配合try/finally语句块来完成.

    4.灵活性
    (1)锁的范围是整个方法或synchronized块部分
    (2)Lock因为是方法调用,可以跨方法,灵活性更大
    5.等待可中断
    (1)不可中断,除非抛出异常(释放锁方式:1.代码执行完,正常释放锁;2.抛出异常,由JVM退出等待)
    (2)持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,(方法:
    1.设置超时方法 tryLock(long timeout, TimeUnit unit),时间过了就放弃等待;
    2.lockInterruptibly()放代码块中,调用interrupt()方法可中断,而synchronized不行)

    6.是否公平锁
    (1)非公平锁
    (2)两者都可以,默认非公平锁,构造器可以传入boolean值,true为公平锁,false为非公平锁.
    ps:适用情况
    资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好
    ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

    7.便利性
    (1)Synchronized的使用比较方便简洁,由编译器去保证锁的加锁和释放
    (2)需要手工声明来加锁和释放锁.

    Synchronized原理:

    Synchronized进过编译,会在同步块的前后分别形成monitorenter和monitorexit这个两个字节码指令。在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计算器加1,相应的,在执行monitorexit指令时会将锁计算器就减1,当计算器为0时,锁就被释放了。如果获取对象锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

    monitorenter :

    每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
    1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
    2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
    3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

    monitorexit:

    执行monitorexit的线程必须是objectref所对应的monitor的所有者。
    指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
    (monitorenter和monitorexit其实是同步代码块中的字节码实现,而在同步方法中使用的是方法表中的ACC_Synchronized标志)
    Synchronized原理详细参考

    ReentrantLock

    由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:
    1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。通过lock.lockInterruptibly()来实现这个机制。
    2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通公平锁、非公平锁的创建方式:

    //创建一个非公平锁,默认是非公平锁
    Lock lock = new ReentrantLock();
    Lock lock = new ReentrantLock(false);
     //创建一个公平锁,构造传参
    trueLock lock = new ReentrantLock(true);
    

    3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

    相关文章

      网友评论

          本文标题:ReentrantLock和Synchronized

          本文链接:https://www.haomeiwen.com/subject/dtgwqhtx.html