synchronized与ReentrantLock的区别
说到synchronized与ReentrantLock,我们都知道,他们是java并发编程很重要的技术。他们可以帮助我们保证编程过程中数据的正确性,也就是我们常说的线程安全。
线程安全通常是多个线程在执行一段代码时采用锁机制实现的。
介绍锁机制前,先了解一下锁的分类。
从设计理念来分,有悲观锁和乐观锁。
悲观锁:认为每次拿数据时,别的线程都抢占修改,所以读写时,将数据进行锁定。
乐观锁:认为每次拿数据时,别的线程并不会抢占修改,所以拿数据处理时不上锁,处理完毕后,将处理完的数据写回内存时,进行判断,如果与预期一致则进行更新,不一致则更新失败。此处使用的时CAS(compare and swap)机制。
从锁的现象可分为公平锁和非公平锁
公平锁:某一线程释放资源后,按顺序唤醒下一个等待的线程,他们之间是排队的,有序的,每个线程都能获得执行的机会。在公平锁的情况下,它的好处是资源平均分配不会有线程饿死的情况。缺点是它的按顺序唤醒线程的开销过大,执行性能不高。
非公平锁:线程执行完毕后,接下来执行的线程进行抢占资源,谁抢到了,谁就执行,没有抢到的就滚去排队。他们之间是不排队的,无序的。每个线程获取执行的机会不相等,所以叫非公平锁。非公平锁的好处是执行效率高,谁抢到就谁用,不会按顺序唤醒线程,缺点是资源分配随机性强,可能会出现线程饿死的情况。
从锁的状态来分可分为偏向锁和重量级锁。
这3种锁其实是有关联的。偏向锁是某一线程持有锁时,等他用完锁以后再想用时,它在其他的线程中是优先级最高的,它可以一直持有锁。
而某一线程持有偏向锁,当其他线程来访问时获取不到锁,就会一直自旋,等待获取锁。此时偏向锁升级为了轻量级锁。
当线程一直获取不到锁,自旋一定的次数,线程就会阻塞。该锁就会升级为重量级锁。升级为重量级锁以后,当有线程再过来访问时,都会被阻塞,直到获取锁。
synchronized与ReentrantLock默认都是非公平锁,在java6以前,synchronized是重量级锁,所以那时候一般没人用。后来优化后好多了。
ReentrantLock可以通过修改配置修改为公平锁,但是synchronized就一直是非公平锁。
当使用 new ReentrantLock(true) 时,可以创建公平锁,如下源码所示:
synchronized依赖于JVM,而ReentrantLock依赖于API,它是一个类,底层其实调用Lock接口,在此接口上做了实现,ReentrantLock需要显式的加锁和解锁。
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
}
ReentrantLock相比较synchronized而言增加了一些功能。
- 等待可中断。ReentrantLock提供了一种能够钟断等待锁的线程机制,能够使等待的线程,先中断等待去做别的事情,使用Lock LockInterruptibly()实现
- 可实现公平锁。通过ReentrantLock类的ReentrantLock(boolean fair)构造方法实现
- 可选择性通知,唤醒线程(锁可以绑定多个条件)。该功能需要借助condition接口于new Condition()方法。而synchronized需要使用wait(),notify(),notifyAll()方法实现等待/通知的机制
标签:Java,synchronized,Lock,ReentrantLock,基础,面试,线程,公平,lock From: https://www.cnblogs.com/stying/p/17253571.html