private volatile int state;
/**
* Returns the current value of synchronization state.
* This operation has memory semantics of a {@code volatile} read.
* @return current state value
*/
protected final int getState() {
return state;
}
/**
* Sets the value of synchronization state.
* This operation has memory semantics of a {@code volatile} write.
* @param newState the new state value
*/
protected final void setState(int newState) {
state = newState;
}
/**
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a {@code volatile} read
* and write.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that the actual
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
在一些线程协调的场景中,一个线程在进行某些操作的时候其他的线程都不能执行该操作,比如持有锁时的操作,在同一时刻只能有一个线程持有锁,我们把这种情景称为独占模式;在另一些线程协调的场景中,可以同时允许多个线程同时进行某种操作,我们把这种情景称为共享模式。
修改state字段代表的同步状态来实现多线程的独占模式或者共享模式。
for example ,**独占模式**下,把state设置为0,每当每个线程要进行某项独占操作.都需要判断status的值是否为0.不为0已经有别的线程进入,那么本线程就需要阻塞等待,如果是0就把state设置为1,本线程进入同步块进行操作,(判断是否为0,并且改为1的过程用cas实现来保证原子性)这个过程叫做:尝试获取同步状态,执行完任务之后把1改为0,会通知外面阻塞等待的线程
共享模式下 允许10个线程同时执行,超过这个数量的线程就需要阻塞等待,我们就可以把state的初始值设置为10,一个线程 尝试获取同步状态的意思就是先判断state的值是否大于0,如果不大于0的话意味着当前已经有10个线程在同时执行该操作,本线程需要阻塞等待,如果state的值大于0,那么可以把state的值减去1后执行该操作.每个线程完成操作的时候需要释放同步状态,也就是把state的值加1,并通知后续等待的线程,
|
| 方法名 | 描述 |
|---|---|
| protected boolean tryAcquire(int arg) | 独占式的获取同步状态,获取成功返回true,否则false |
| protected boolean tryRelease(int arg) | 独占式的释放同步状态,释放成功返回true,否则false |
| protected int tryAcquireShared(int arg)– | -共享式的获取同步状态,获取成功返回true,否则false- |
| protected boolean tryReleaseShared(int arg) | 共享式的释放同步状态,释放成功返回true,否则false |
| protected boolean isHeldExclusively() | isHeldExclusively() 在独占模式下,如果当前线程已经获取到同步状态,则返回 true;其他情况则返回 false |
try Acquire在 AQS中的实现
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
不同的同步工具针对的具体并发场景不同,所以如何获取同步状态和如何释放同步状态是需要我们在自定义的AQS子类中实现的,如果我们自定义的同步工具需要在独占模式下工作,那么我们就重写tryAcquire、tryRelease和isHeldExclusively方法,如果是在共享模式下工作,那么我们就重写tryAcquireShared和tryReleaseShared方法。比如在独占模式下我们可以这样定义一个AQS子类:
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
/**
* @Author: fanbopeng
* @Date: 2019/6/14 13:02
* @Description: 独占模式下
*/
public class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0,1);
}
@Override
protected boolean tryRelease(int arg) {
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState()==1 ;
}
}
##同步队列
AQS中还维护了yi一个所谓的同步队列.
static final class Node {
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread; //因为维护了这个Thread变量,表明每一个节点都代表一个线程,当一个线程获取同步状态失败之后,就把这个线程包装成Node节点插入到这个同步队列当中,当获取同步状态成功的线程释放同步状态时候.,同时会通知队列中下一个未获取到的节点,让该节点的线程再去获取同步状态
Node nextWaiter;
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
}
在AQS中引用了它
一个头结点,一个尾结点
private transient volatile Node head;
private transient volatile Node tail;
独占式同步状态获取与释放
前边说过,获取和释放同步状态的方式是由我们自定义的,在独占模式需要我们定义AQS的子类并且重写下边这些方法:
protected boolean tryAcquire(int arg)
protected boolean tryRelease(int arg)
protected boolean isHeldExclusively()
void acquire(int arg)
独占式获取同步状态,如果获取成功则返回,如果失败则将当前线程包装成Node节点插入同步队列中。
void acquireInterruptibly(int arg)
与上个方法意思相同,只不过一个线程在执行本方法过程中被别的线程中断,则抛出InterruptedException异常。
boolean tryAcquireNanos(int arg, lng nanos)
在上个方法的基础上加了超时限制,如果在给定时间内没有获取到同步状态,则返回false,否则返回true。
boolean release(int arg)
独占式的释放同步状态。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode); //构造一个新节点
Node pred = tail;
if (pred != null) { //尾节点不为空,插入到队列最后
node.prev = pred;
if (compareAndSetTail(pred, node)) { //更新tail,并且把新节点插入到列表最后
pred.next = node;
return node;
}
}
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { //tail节点为空,初始化队列
if (compareAndSetHead(new Node())) //设置head节点
tail = head;
} else { //tail节点不为空,开始真正插入节点
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); //获取前一个节点
if (p == head && tryAcquire(arg)) { 前一个节点是头节点再次尝试获取同步状态
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
Node.CANCELLED
1
节点对应的线程已经被取消了(我们后边详细会说线程如何被取消)
Node.SIGNAL
-1
表示后边的节点对应的线程处于等待状态
Node.CONDITION
-2
表示节点在等待队列中(稍后会详细说什么是等待队列)
Node.PROPAGATE
-3
表示下一次共享式同步状态获取将被无条件的传播下去(稍后再说共享式同步状态的获取与释放时详细唠叨)
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L); //调用底层方法阻塞线程
setBlocker(t, null);
}
这个就表示立即阻塞线程,这是一个底层方法,我们程序员就不用关心操作系统时如何阻塞线程的了。
呼~至此,我们在独占模式下跑完了一个获取不到同步状态的线程是怎么被插入到同步队列以及被阻塞的过程。
##共享式同步状态获取与释放
共享式与独占式最大的区别就是在同一时刻是否有多个线程可以同时获取到同步状态
void acquireShared(int arg)共享式获取同步状态,如果失败则将当前线程包装成Node节点插入同步队列中。。
void acquireSharedInterruptibly(int arg)与上个方法意思相同,只不过一个线程在执行本方法过程中被别的线程中断,则抛出InterruptedException异常
boolean tryAcquireSharedNanos(int arg, long nanos)|在上个方法的基础上加了超时限制,如果在给定时间内没有获取到同步状态,则返回false,否则返回true。
boolean releaseShared(int arg)|共享式的释放同步状态。|
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
这个方法会调用我们自定义的AQS子类中的tryAcquireShared方法去获取同步状态,只不过tryAcquireShared的返回值是一个int值,该值不小于0的时候表示获取同步状态成功,则acquireShared方法直接返回,什么都不做;如果该返回值大于0的时候,表示获取同步状态失败,则会把该线程包装成Node节点插入同步队列,插入过程和独占模式下的过程差不多,我们这就不多废话了。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
1816

被折叠的 条评论
为什么被折叠?



