- 常用的显示锁,基本上都是实现Lock接口,Lock接口常用的方法:
lock() 获取锁
unlock() 释放锁
tryLock() 尝试获取锁
- 使用显示锁的范式:
try{
lock.lock();
}finally{
lock.unlock();
}
- lock和synchronized的比较
- synchronized代码简洁
- Lock 可以中断,尝试获取,超时获取
- 尽量使用Synchronized,Synchronized默认实现了可重复锁。
- 可重复锁的概念:若一个程序或子程序可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(reentrant或re-entrant)的。
- 在Java内部,同一个线程调用自己类中其他synchronized方法/块时不会阻碍该线程的执行,同一个线程对同一个对象锁是可重入的,同一个线程可以获取同一把锁多次,也就是可以多次重入。原因是Java中线程获得对象锁的操作是以线程为单位的,而不是以调用为单位的。
- synchronized可重入锁的实现
每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁而调用相应方法。 当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁, 就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。
- 公平锁和非公平锁(ReentrantLock实例化的时候指定,默认是非公平锁)
- 如果在时间上,先对锁进行获取的请求,一定先被满足,这就是公平的,反之就是非公平的
- 非公平锁的效率一般来说更高,比如前一个线程挂起,后一个线程无法请求到锁
- ReadWriteLock和ReentrantReadWriteLock
- ReentrantLock和synchronized都是排他锁或者说独占锁,同一个时刻只允许一个线程拥有锁
- ReadWriteLock同一时刻允许多个读线程同时访问,但是写线程访问的时候,所有的读和写都被阻塞。即write是独占锁,read是共享锁
- 适用于读多写少的情况
- Condition:通过lock.newCondition创建对象
- await():等待。将线程从同步队列转移到等待队列中
- signal():放开,将线程从等待队列转移到同步队列中。尽量使用signal,不要使用signalAll(),因为锁只能被一个线程拿到。
- 一个Lock可以对应多个Condition
- LockSupport工具
- 作用: 构建同步组件的基础工具
- park:阻塞一个线程
- unpark:唤醒一个线程
- AbstractQueuedSynchronizer深入分析
- 什么是AQS?学习它的必要性
- AQS使用方式和其中的设计模式:模板方法设计模式
1)模板方法
//独占式获取锁
acquire(int arg)
acquireInterruptibly(int arg)
tryAcquireNanos(int arg, long nanosTimeout
// 共享式获取锁
acquireShared(int arg)
acquireSharedInterruptibly(int arg)
tryAcquireSharedNanos(int arg, long nanosTimeout)
// 独占式释放锁
release(int arg)
//共享式释放锁
releaseShared(int arg)
2)需要子类覆盖的流程方法
// 独占式尝试获取
tryAcquire
// 独占式释放
tryRelease
// 共享式获取
tryAcquireShared
// 共享式释放
tryReleaseShared
// 这个同步器是否占用
isHeldExclusively()
3)同步状态state:
getState:获取当前的同步状态
setState:设置当前的同步状态
compareAndSetState:使用CAS设置状态,保证状态设置的原子性
- AQS中的数据结构-节点和同步队列
Node:封装并发线程的类
CANCELLED:线程等待超时或者中断了,节点被移除
SIGNAL:后续的节点等待状态,当前节点,通知后面的节点去运行
CONDITION: 当前节点处于等待队列
PROPAGATE:传播
0表示当前节点初始状态
网友评论