synchronized关键字与java.util.concurrent.locks的lock接口synchronized关键字
Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
lock.unlock();
}
不要将获取锁的过程写在try块中,因为如果在获取锁(自定义锁的实现)时发生了异常,异常抛出的同时,也会导致锁无故释放。
2.1.简介
AbstractQueuedSynchronizer,队列同步器,是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。
同步器提供的模板方法基本上分为3类:
独占锁就是在同一时刻只能有一个线程获取到锁,而其他获取锁的线程只能处于同步队列中等待,只有获取锁的线程释放了锁,后继的线程才能够获取锁。
2.2 实现
同步队列
获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。没有成功获取同步状态的线程将会成为节点加入该队列的尾部。节点的属性类型与名称以及描述
同步队列结构图
CAS设置尾结点图
首节点的设置图
中断操作时,线程不会从同步队列中移出。acquireQueued(final Node node,int arg)方法,只有前驱节点是头节点才能够尝试获取同步状态原因
- 过早通知:指前驱节点不是头节点的线程由于中断而被唤醒
节点自旋获取同步状态图
/**
* 1.tryAcquire保证线程安全的获取同步状态(CAS)
* 2.假如1获取失败,以独占式Node.EXCLUSIVE构造同步节点,即是一个同个时刻只能有一个线程成功获取同步状态,并通过addWaiter(Node node)方法将该节点加入到同步队列的尾部
* 3.调用acquireQueued(Node node,intarg)方法(自旋),使得该节点以“死循环”的方式获取同步状态
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
总结
- 在获取同步状态时,同步器维护一个同步队列,获取状态失败的线程都会被加入到队列中并在队列中进行自旋;
- 移出队列(或停止自旋)的条件是前驱节点为头节点且成功获取了同步状态。在释放同步状态时同步器调用
tryRelease(int arg)方法释放同步状态,然后唤醒头节点的后继节点。
doAcquireNanos(int arg,long nanosTimeout)方法可以例子:
public class Cache { static Map<String, Object> map = new HashMap<String, Object>(); static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); static Lock r = rwl.readLock(); static Lock w = rwl.writeLock(); // 获取一个key对应的value public static final Object get(String key) { r.lock(); try { return map.get(key); } finally { r.unlock(); } } // 设置key对应的value,并返回旧的value public static final Object put(String key, Object value) { //其他线程对于读锁和写锁的均被阻塞 w.lock(); try { return map.put(key, value); } finally { w.unlock(); } } // 清空所有的内容 public static final void clear() { w.lock(); try { map.clear(); } finally { w.unlock(); } } }
写锁:支持重进入的排它锁。
读锁已经被获取(读状态不为0)或者该线程不是已经获取写锁的线程,则当前线程进入等待状态。存在读锁,则写锁不能被获取。
锁降级:指的是写锁降级成为读锁。
使用场景:
public void processData() { readLock.lock(); if (!update) { // 必须先释放读锁 readLock.unlock(); // 锁降级从写锁获取到开始 writeLock.lock(); try { if (!update) { // 准备数据的流程(略) update = true; } readLock.lock(); } finally { writeLock.unlock(); } // 锁降级完成,写锁降级为读锁 } try { // 使用数据的流程(略) } finally { readLock.unlock(); } }
Object的监视器方法与Condition接口的对比
原文:https://www.cnblogs.com/lycsmzl/p/13213517.html