ReentrantLock原理(CAS+AQS)
CAS+AQS队列来实现:
先通过CAS尝试获取锁, 如果此时已经有线程占据了锁,那就加入AQS队列并且被挂起
当锁被释放之后, 排在队首的线程会被唤醒CAS再次尝试获取锁
如果是非公平锁, 同时还有另一个线程进来尝试获取可能会让这个线程抢到锁;
如果是公平锁, 会排到队尾,由队首的线程获取到锁。
1. CAS
原理
内存值V,旧的预期值A,要修改的新值B,当A=V时,将内存值修改为B,否则什么都不做;缺点
ABA问题
如果CAS失败,自旋会给CPU带来压力
只能保证对一个变量的原子性操作,i++这种是不能保证的
CAS在java中的应用
Atomic系列
2. AQS (AbstractQueuedSynchronizer)
队列同步器,这是实现 ReentrantLock 的基础
原理
AQS 有一个 state 标记位,值为1 时表示有线程占用,其他线程需要进入到同步队列等待,同步队列是一个双向链表。
Node内部类构成的一个双向链表结构的同步队列,通过控制(volatile的int类型)state状态来判断锁的状态,对于非可重入锁状态不是0则去阻塞;
对于可重入锁如果是0则执行,非0则判断当前线程是否是获取到这个锁的线程,是的话把state状态+1,比如重入5次,那么state=5。 而在释放锁的时候,同样需要释放5次直到state=0其他线程才有资格获得锁
AQS两种资源共享方式
Exclusive:独占,只有一个线程能执行,如ReentrantLock
Share:共享,多个线程可以同时执行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier
synchronized与ReentrantLock区别
都是可重入锁; ReentrantLock是显示获取和释放锁,synchronized是隐式;
ReentrantLock更灵活可以知道有没有成功获取锁,可以定义读写锁,是api级别,synchronized是JVM级别;
ReentrantLock可以定义公平锁;Lock是接口,synchronized是java中的关键字
ReentrantLock 是如何实现可重入性的?
内部自定义了同步器 Sync,加锁的时候通过CAS 算法 ,将线程对象放到一个双向链表中,每次获取锁的时候 ,看下当前维护的那个线程ID和当前请求的线程ID是否一样,一样就可重入了;
ReentrantLock如何避免死锁?
响应中断lockInterruptibly()
可轮询锁tryLock()
定时锁tryLock(long time)