线程状态及状态转换图
线程状态 | 含义 | 诱发动作 |
---|---|---|
NEW | 新建线程对象,但尚未启动(start())。 | new Thread() |
RUNNABLE | 一个可运行的线程,包含就绪(等待系统调度分配cpu时间片)和运行中(获得cpu时间片开始运行)两种状态。 | Ready: Thread.yield() , Running: 被线程调度器选择 |
BLOCKED | 被阻塞等待监视器锁。 | IO阻塞, 等待进入同步代码块或方法 |
WAITING | 无限期等待另一个线程执行一个特定操作(通知或中断)。 | Object.wait() , Thread.join() , LockSupport.park() |
TIMED_WAITING | 具有指定等待时间,可以在指定的时间内自行返回。 | Thread.sleep(long) , Object.wait(long timeout) , Thread.join(long timeout) , LockSupport.parkNanos(Object blocker, long nanos) , LockSupport.parkUntil(Object blocker, long deadline) |
TERMINATED | 线程已经执行完毕。 | run() 退出, Thread.stop() , 线程中断退出, 阻塞IO被关闭 |
显示锁和隐式锁的区别
synchronized | Lock |
---|---|
关键字,隐式获取/释放锁 | 接口,显式获取/释放锁 |
代码简单,被大多程序员广泛使用,默认推荐 | 代码稍复杂,在try块外获取锁,在finally块中释放锁,迫于性能调优时再用 |
- | 可尝试非阻塞获取锁(线程尝试获取锁,若锁未被其他线程持有,则成功获取并持有锁) |
- | 可中断获取锁(获取到锁的线程能够响应中断,当该线程被中断时,中断异常将被抛出,同时锁释放) |
- | 可超时获取锁(在指定的截止时间之前获取锁,若超时仍无法获取锁,则返回) |
锁是面向使用者的,它定义了使用者与锁交互的接口(比如可以允许两个线程并行访问),隐藏了实现细节; 同步器面向锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。
同步器可重写的方法
方法 | 描述 |
---|---|
boolean tryAcquire(int arg) | 独占式获取同步状态。实现该方法需要查询当前同步状态并判断是否符合预期,然后再进行CAS设置同步状态。 |
boolean tryRelease(int arg) | 独占式释放同步状态。等待获取同步状态的线程将有机会获取同步状态。 |
int tryAcquireShared(int arg) | 共享式获取同步状态。返回大于等于0的值表示获取成功,否则获取失败。 |
boolean tryReleaseShared(int arg) | 共享式释放同步状态。 |
boolean isHeldExclusively() | 同步状态是否在独占模式下被线程占用。 |
同步器提供的便捷方法
方法 | 描述 |
---|---|
void acquire(int arg) | 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回。否则,将会进入同步队列等待,该方法忽略中断。 |
void acquireInterruptibly(int arg) throws InterruptedException | 同上,但该方法响应中断,在同步队列中等待的线程可以被中断,会抛出InterruptedException并返回。 |
boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException | 同上且增加了超时限制,如果在超时时间内没有获取到同步状态将返回false,否则返回true。 |
boolean release(int arg) | 独占式释放同步状态。释放后会唤醒同步队列中的第一个节点所包含的线程。 |
void acquireShared(int arg) | 共享式获取同步状态,若同步未获取获取成功则会进入同步队列等待。与独占式获取主要区别在同一时刻可以有多个线程获取同步状态。 |
void acquireSharedInterruptibly(int arg) throws InterruptedException | 同上,但该方法响应中断 |
boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException | 同上且增加了超时限制 |
boolean releaseShared(int arg) | 共享式释放同步状态。 |
同步器中节点的含义
AQS#Node的属性 | 含义 |
---|---|
int waitStatus | 状态字段,仅可能取以下几种值:状态字段,仅可能取以下几种值:SIGNAL : 当前节点的后继节点是(或不久的将来)阻塞的(通过park),因此当前节点释放或取消同步状态时必须通知(通过unpark)后继节点,为了避免竞争激烈,acquire方法必须首先表明他们需要一个启动信号,然后原子性重试获取同步状态,最后在失败时阻塞。CANCELLED : 由于取消或中断,节点取消获取同步状态,节点进入该状态后将不会再发生变化。注意取消节点的线程永远不会再阻塞。CONDITION : 此节点当前处于条件队列中。直到被转移(signal/signalAll)才会被加入到同步队列中,转移后状态将被设置为0。PROPAGATE : releaseShared应该传播给其他节点。在doReleaseShared中设置(仅限头节点)以确保继续传播,即使其他操作已经介入。0 : 初始化状态 |
Node prev | 前驱节点,当节点加入到同步队列时被设置(CAS尾部加入) |
Node next | 后继节点 |
Thread thread | 获取同步状态的线程 |
Node nextWaiter | 条件队列中的后继节点,或特殊值SHARED 。因为条件队列只有在保持独占模式时才被访问,所以我们只需要一个简单的链接队列来在节点等待条件时保存节点。然后将它们转移到同步队列中以重新获取同步状态。并且因为condition只能是独占的,所以我们通过使用SHARED特殊值来指示共享模式。 |
同步队列和等待队列图示
AQS超时获取锁的源代码
// acquire,acquireInterruptibly与此大同小异
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) || // 先尝试获取一次
doAcquireNanos(arg, nanosTimeout); // 尝试失败,将其包装成Node,放入等待队列,并等待前驱节点唤醒
}
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE); // 包装成独占模式等待者
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) { // 若是头节点,尝试获取同步状态,成功则将自己设置成头节点
setHead(node);
p.next = null; // 释放前驱节点引用,便于GC回收原头节点
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L) // 超时仍未获取到,则返回fasle
return false;
if (shouldParkAfterFailedAcquire(p, node) && // 如果前驱节点状态(SIGNAL)正常,则等待;若已取消(CANCELLED)为其寻找一个正常的前驱节点;否则CAS设置前驱节点状态为正常态
nanosTimeout > spinForTimeoutThreshold) // 超时时间大于阈值,则使用parkNanos超时等待;否则采用高速自旋重试
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted()) // 被唤醒发现线程中断,则抛出中断异常
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node); // 超时或中断退出时,则取消获取同步状态
}
}
AQS释放锁的源代码
public final boolean release(int arg) {
if (tryRelease(arg)) { // 尝试释放同步状态
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 释放成功,唤醒头节点的后继节点
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 将头节点状态修改为0
Node s = node.next;
if (s == null || s.waitStatus > 0) { // 头节点不存在后继节点或后继节点已取消获取同步状态
s = null;
// 从前往后寻找不一定能找到刚刚加入队列的后继节点, 因为在Node addWaiter(Node mode)中,是先CAS设置尾节点,再设置前驱节点和尾节点的引用关系
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0) // 从队尾往前找,找到第一个需要唤醒的节点
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); // 唤醒线程
}
AQS超时获取锁的流程图
ReentrantLock非公平获取锁源代码
// NonfairSync类中
final void lock() {
if (compareAndSetState(0, 1)) // 非公平锁直接尝试获取锁
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// Sync父类中
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // 目前没有其他线程获得锁,当前线程就可以尝试获取锁
if (compareAndSetState(0, acquires)) { // CAS修改同步状态
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { // 当前线程持有锁,支持重入
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc); // 已持有锁,可直接修改
return true;
}
return false; // 修改失败返回false
}
ReentrantLock公平获取锁源代码
// FairSync类中
final void lock() {
acquire(1);
}
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;
}
ReentrantLock释放锁的源代码
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread()) // 当前线程未持有锁,不可释放
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // 由于锁可重入,当同步状态等于0时,才代表真正释放掉
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
ReentrantReadWriteLock获取释放锁源代码
/*以高16位表示所有线程获取读锁数,以低16位表示单个线程获取写锁数 */
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 0x00010000
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 低16位全为1
static int sharedCount(int c) { return c >>> SHARED_SHIFT; } // 根据同步状态计算已持有的读锁数
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } // 根据同步状态计算已持有的写锁数
protected final boolean tryAcquire(int acquires) { // 获取独占锁(写锁)
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c); // 持有的写锁数
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
// 如果同步状态不为0但是写锁数为0,代表持有读锁(不为0)
if (w == 0 || current != getExclusiveOwnerThread()) // 已持有读锁或不是当前线程持有写锁,均不可再获取写锁
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT) // 获取写锁数超限
throw new Error("Maximum lock count exceeded");
setState(c + acquires); // 是当前线程持有写锁,可重入获取
return true;
}
if (writerShouldBlock() || // 公平模式下需要判断同步队列中是否有前驱节点在等待
!compareAndSetState(c, c + acquires)) // 无线程获取写锁,CAS获取锁
return false;
setExclusiveOwnerThread(current); // 获取成功
return true;
}
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively()) // 非持有写锁的线程不可释放写锁
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0; // 因为可重入,当同步状态低16位全为0,才代表成功释放
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
protected final int tryAcquireShared(int unused) { // 获取共享锁(读锁)
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current) // 写锁已被持有但不是当前线程,获取读锁阻塞
return -1;
int r = sharedCount(c); // 持有的读锁数
if (!readerShouldBlock() && // 公平模式下需要判断同步队列中是否有前驱节点在等待
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) { // 无线程持有写锁,所有线程都可获取读锁
if (r == 0) { // 无线程持有读锁,标记当前线程首次获取读锁1次
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) { // 有线程持有读锁,若当前线程是首次获取读锁的线程,则增加读锁持有数
firstReaderHoldCount++;
} else { // 有线程持有读锁,但不是首次获取读锁的线程,则初始化线程相应读锁持有数
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) // 无缓存或缓存的计数不是当前线程
cachedHoldCounter = rh = readHolds.get(); // 获取线程本地缓存
else if (rh.count == 0) // 有缓存且当前线程第一次获取锁,则初始化线程(锁计数)本地缓存
readHolds.set(rh);
rh.count++; // 当前线程持有读锁数加1
}
return 1;
}
return fullTryAcquireShared(current); // 自旋获取读锁,用于应对首次尝试CAS未命中和重入读锁的情况
}
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) { // 当前线程是第一个获取读锁的线程
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1) // 线程仅持有1个读锁,释放后即无第一个读线程
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) { // 只获取一次读锁,则直接移除,
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count; // 减少缓存计数信息
}
for (;;) { // 自旋释放读锁
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;
}
}
参考: 《Java并发编程的艺术》