同步器的设计是基于模板方法模式的,如果需要自定义同步器一般的方式是这样:
1.使用者继承AbstractQueuedSynchronizer并重写指定的方法。
2.将AQS组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。模板方法模式是基于”继承“的,主要是为了在不改变模板结构的前提下在子类中重新定义模板中的内容以实现复用代码。举个很简单的例子假如我们要去一个地方的步骤是:购票buyTicket()->安检securityCheck()->乘坐某某工具回家ride()->到达目的地arrive()。我们可能乘坐不同的交通工具回家比如飞机或者火车,所以除了ride()方法,其他方法的实现几乎相同。我们可以定义一个包含了这些方法的抽象类,然后用户根据自己的需要继承该抽象类然后修改 ride()方法。
AQS使用了模板方法模式,自定义同步器时需要重写模板方法:
boolean isHeldExclusively() //该线程是否正在独占资源。只有用到condition才需要去实现它。
boolean tryAcquire(int) //独占锁,尝试获取资源。
boolean tryRelease(int) //独占锁。尝试释放资源。
int tryAcquireShared(int) //共享锁。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
boolean tryReleaseShared(int)//共享锁。尝试释放资源。
ReentrantLock
独占锁,可重入
属性
private final Sync sync;
实现Lock接口,重写方法
public void lock() {
sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
构造函数
//true:公平锁,false:非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
非公平锁
加锁
1.ReentrantLock:
public void lock() {
sync.acquire(1);//进入2
}
2.AbstractQueuedSynchronizer:
public final void acquire(int arg) {
if (!tryAcquire(arg) && //尝试获取锁成功返回true,进入3
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//当线程失败时加入同步等待队列,首先进入5返回Node,进入6
selfInterrupt();//加入队列失败,中断线程
}
3.ReentrantLock中非公平锁的实现
static final class NonfairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);//进入4
}
}
4.ReentrantLock中Syn的实现,尝试获取锁
final boolean nonfairTryAcquire(int acquires) {//非公平锁获取
final Thread current = Thread.currentThread();//获取当前线程
int c = getState();//获取state
if (c == 0) {//当前没有线程获取锁
//此处与公平锁不同,直接尝试修改state
if (compareAndSetState(0, acquires)) {//CAS设置state=1
setExclusiveOwnerThread(current);//设置独占线程为当前线程
return true;//返回获取到锁
}
}
//当前锁已被线程获取
else if (current == getExclusiveOwnerThread()) {//获取的线程是当前线程
//实现了可重入锁
int nextc = c + acquires;//修改state
if (nextc < 0) // 溢出
throw new Error("Maximum lock count exceeded");
setState(nextc);//覆盖state
return true;//返回获取锁
}
return false;//没有获取锁
}
5.AbstractQueuedSynchronizer:创建节点
private Node addWaiter(Node mode) {
Node node = new Node(mode);//新建节点
for (;;) {
Node oldTail = tail;
if (oldTail != null) {//当前队列中有节点
node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {//CAS设置tail
oldTail.next = node;
return node;
}
} else {//初始化队列
initializeSyncQueue();
//再次进入循环将节点加入队列
}
}
}
6.节点加入等待队列
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
//只有获得锁才会退出循环
for (;;) {
final Node p = node.predecessor();//获得前驱
if (p == head && tryAcquire(arg)) {//队列中等待的第一个节点,获取锁成功
setHead(node);//将当前线程的节点设置为头结点
p.next = null; // help GC
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node))//阻塞当前进程
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
解锁
1.ReentrantLock:
public void unlock() {
sync.release(1);//进入2
}
2.AbstractQueuedSynchronizer:
public final boolean release(int arg) {
if (tryRelease(arg)) {//进入3
Node h = head;
if (h != null && h.waitStatus != 0)//队列不为空
unparkSuccessor(h);//唤醒被阻塞的线程
return true;
}
return false;
}
3.ReentrantLock:尝试释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;//计算修改后的state
if (Thread.currentThread() != getExclusiveOwnerThread())//判断当前线程是否是独占线程
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//锁未重入
free = true;
setExclusiveOwnerThread(null);//消除持有锁线程
}
setState(c);//更新state
return free;
}
公平锁
tryAcquire()与非公平锁不同
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&//确保当前线程是第二个节点(即等待中的第一个节点)
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
公平锁与非公平锁:
非公平锁模式:新的线程可能会抢占在排队的线程,但是等待的线程仍保证按序执行
公平锁模式:新的线程不会抢占锁,直接加入等待队列
ReentrantReadWriteLock
频繁读取及少量修改的情况下可以较好的提高性能。
回顾CopyOnWriteArrayList,只提供弱一致性。如果需要数据实时更新可以使用ReentrantReadWriteLock。
AQS中state变量是int类型,32位。在ReentrantReadWriteLock中将32位分成两部分,高16位表示持有读锁的线程数,低16位表示写锁的重入数。
1.不允许写线程和读线程、写线程和写线程同时访问。
2.允许锁降级:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不允许的。
属性
//读锁,共享锁
private final ReentrantReadWriteLock.ReadLock readerLock;
//写锁,独占锁
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
构造方法
//默认是非公平锁
public ReentrantReadWriteLock() {
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
Synchronized与ReentrantLock
1.Synchronized是JVM实现,Synchronized由JDK实现
2.
Synchronized | Synchronized | |
---|---|---|
实现 | JVM | JDK |
性能 | JDK 1.6以后对Synchronized进行了优化 | - |
等待可中断 | 不可以 | 可以 |
公平锁 | 非公平 | 都可以 (默认非公平) |
绑定条件 | wait()/notify()可以实现一个条件 | 可以绑定多个Condition对象 |
释放 | JVM执行 | finally中显示释放 |
Synchronized与CAS
Synchronized是悲观锁,总是假设数据会被修改。适合写多的情况。
CAS是乐观锁,不会上锁,在更新时会判断数据有没有被修改,适合写少的情况。
乐观锁存在的问题
1.ABA(AtomicStampedReference 类可以解决)
2.自旋开销
3.只保证一个共享变量的原子操作(AtomicReference类可以解决)