【多线程】synchronized与Lock的区别
相同点
都是用来保护资源线程安全的
都可以保证可见性
可见性大致可以这样理解,线程A的加解锁的过程会对B线程完全可见,也就是说,线程A的加锁和解锁当时的锁持有的状态,线程B是可以获取到的,这也就体现到了hanppens-before针对synchronized的一个原则。
对于Lock而言,他也是可以保证线程的可见性,例如下面示例的流程
关于可见性,可以详细了解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会跟安全。