【多线程】synchronized与Lock的区别

邓敏 2022年03月23日 57次浏览

相同点

都是用来保护资源线程安全的

都可以保证可见性

可见性大致可以这样理解,线程A的加解锁的过程会对B线程完全可见,也就是说,线程A的加锁和解锁当时的锁持有的状态,线程B是可以获取到的,这也就体现到了hanppens-before针对synchronized的一个原则。
image.png

对于Lock而言,他也是可以保证线程的可见性,例如下面示例的流程
image.png

关于可见性,可以详细了解Java内存模型的知识。

都拥有可重入的特点

关于可重入锁,他表示的是一个线程A他获取到了B锁,如果线程A在运行的过程中想要再获取B锁的同时,不需要B锁再释放锁资源就能够获取到,那么他就是一个可重入的锁,这个synchronized与ReentrantLock都具有相同的特点。
关于可重入锁的知识可以看一下【多线程】锁的七大分类

不同点

用法区别

Lock的锁具有灵活性,他可以在A先加锁,B再加锁的时候,实现A先解锁,B再解锁。这里的解锁顺序可以完全由我们自行控制。

lock1.lock();
lock2.lock();
...
lock1.unlock();
lock2.unlock();

synchronized就无法做到这一点,原因是由于synchronized加锁和解锁的顺序完全是由JVM生成的代码来实现的,属于隐式加锁。所以他的加解锁的顺序一定是要符合后加锁先解锁的原则。

synchronized(obj1){
    synchronized(obj2){
        ...
    }
}

线程锁的共享性

synchronized 锁只能同时被一个线程拥有,但是 Lock 锁没有这个限制。
这个原因是因为synchronized他是内置锁,由JVM实现获取锁和释放锁的原理,我们无法通过代码控制,Lock则可以根据实现不同,有不同的原理,例如ReentrantLock内部是通过AQS来获取和释放锁的。

线程锁的公平性

ReentrantLock可以根据Lock等实现类设置公平或非公平,synchronized则默认是非公平锁,并且无法设置。

性能区别

synchronized在Java5之前性能是非常低的,在Java的研发团队不断的优化和迭代之后,synchronized锁的性不比Lock锁的性能差,因为他优化了很多,比如自适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等。

适用场景

在我们平时日常编写代码中,能不用就最好不用Lock和synchronized锁,因为在许多情况下你可以使用java.util.concurrent包中的机制,他中间就包括了很多锁的处理,也是推荐优先使用工具来来加解锁。

如果你需要使用一些特锁的功能,比如设置公平性或者控制解锁的顺序,那么就可以使用Lock来加解锁,但是如果你用不到Lock的特性,则建议直接使用synchronized,应为这样可以减少代码的数量,并且还会减少出错的概率,因为使用Lock来加锁,就必须finally的去解锁,如果忘记,代码就会出现很大的问题,相比之下,用synchronized会跟安全。