5. Java中的锁

Lock接口

需要显式的获取和释放锁,支持非阻塞的获取锁,支持中断的获取锁,支持超时获取锁;

Synchronzed是自动隐式的获取和释放锁,不支持非阻塞,不支持中断获取,不支持超时,隐式的支持重入锁;

队列同步器

AbstractQueuedSynchronizer(AQS),使用模板方法模式,是用来构建锁的其他同步组件的基础框架,实现了同步状态管理、线程的派对、等待和唤醒等底层操作。

同步队列:内部通过FIFO双向队列来完成同步状态的管理,线程获取同步失败,则构造Node节点(包含当前线程引用、前驱、猴急、等待状态信息)放入队列末尾,并阻塞当前线程;释放同步时,会把队列中的首节点线程唤醒;

队列拥有头结点和尾节点,头结点时获取到同步状态(即锁)的节点,首节点释放锁后会通知后继节点,并设为新的首节点;

加入队列的过程要保证线程安全,提供了CAS方法来设置;

独占式获取同步和释放:获取状态失败的线程加入同步队列并自旋,一直到前驱节点为头结点切获取到状态则退出自旋并移除队列;释放同步时会唤醒头结点的后继节点;

共享式获取同步和释放:独占式访问资源时,其他独占和共享式访问都不被允许;共享式访问时允许其他共享,不允许独占访问;共享式即统一时刻可以有多个线程获取到同步状态;

自定义同步组件一般是同步内部静态类继承AQS实现,在调用内部静态类的方法对外提供;

重入锁

不可重入:当前线程获取到锁,再次获取锁时,没有判断持有锁的线程是否就是当前线程,随即加入同步队列尾部进行等待;

实现可重入: 锁需识别当前获取锁的线程是否为持有锁的线程;线程重复获取n次锁,计数器自增,释放锁自减,直到为0时才最终释放锁;

ReentrantLock实现可重入锁:获取同步状态时判断是否当前持有线程,是则同步状态值加1返回成功,再次获取锁只是增加同步状态值;

公平锁:获取锁的顺序按照同步队列的顺序获取,即等待时间越长越先获取,非公平锁相反,ReentrantLock末尾为非公平锁;

公平锁保证了FIFO原则,但会造成大量的线程切换,非公平锁会造成线程饥饿,但极少的线程切换保证了更大的吞吐量;

读写锁

维护了一对锁,通过一个读锁和一个写锁使得并发性提升:写时不允许写和读,读时允许读不允许写;实现:ReentrantReadWriteLock;

读写状态的设计:AQS中只有一个整形变量表示同步状态,现在又2个锁,通过按位切割来分别保存读写状态,高16位为读状态,低16位为写状态(int4字节共32位),通过位运算快速判断读写状态;

写锁获取与释放:判断读锁是否获取(读状态不为0),或者写锁已经被其他线程获取;释放:同ReentrantLock,释放时减少写状态直到为0;

读锁获取与释放:写状态为0时始终可获取到读锁,否则等待;释放:同ReentrantLock,释放时减少写状态直到为0;

锁降级:获取到写锁的线程,把持住写锁,先获取到读锁,再释放写锁;(不是获取写锁,释放写锁再获取读锁,这是可能会被其他线程写)

锁升级:不支持,因为多个线程获取了读锁,写之后,其他线程读数据有问题;

LockSupport

构成同步组件的基础工具,提供了基本的线程阻塞和唤醒功能:park(超时、到达指定时间、中断、unpark时返回)和unpark;

Condition

实现类似Object的wait/notify机制,与Lock配合实现等待通知模式;调用Condition方法时需要获取关联的Lock锁;

condition.await返回时表示已经获取了锁,返回条件:signal或者被中断;

Condition内部实现复用了AQS,Lock可以有多个Condition,所以Lock有一个同步队列和多个等待队列;

你可能感兴趣的:(5. Java中的锁)