join源码
//注意这里加锁了 锁的对象是当前调用join方法的对象 即this
public final synchronized void join(long millis)throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
//如果线程还在存活 那么死循环
while (isAlive()) {
long delay = millis - now;
//如果带有等待超时时间的join
//并且超时时间已经过去了
//那么直接跳出死循环 join方法结束
if (delay <= 0) {
break;
}
//带有等待超时时间的join
//即使在这里错误被唤醒
//只要线程是alive状态,会重新进入循环继续阻塞等待。
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
线程是如何被阻塞的?又是通过什么方法唤醒的呢?先来看看Thread.join方法做了什么事情
从join方法的源码来看,join方法的本质调用的是Object中的wait方法实现线程的阻塞。但是我们需要知道的是,调用wait方法必须要获取锁,所以join方法是被synchronized修饰的,synchronized修饰在方法层面相当于synchronized(this),this就是previousThread本身的实例。
有很多人不理解join为什么阻塞的是主线程呢?
不理解的原因是阻塞主线程的方法是放在previousThread这个实例作用,让大家误以为应该阻塞previousThread线程。
实际上主线程会持有previousThread这个对象的锁,然后调用wait方法去阻塞,而这个方法的调用者是在主线程中的。所以造成主线程阻塞。
为什么previousThread线程执行完毕就能够唤醒住线程呢?或者说是在什么时候唤醒的?
因为线程执行完毕以后isAlive()是false。 就会跳出死循环。join代码执行结束。
Thread 与 Runnable 的关系
Thread实现了runnable接口重写了run方法并且拥有一个Runnable的变量。
Thread持有一个属性Runnable叫target,调用start方法时,判断target是否存在,如果存在优先调用target的run方法,否则调用子类重写的run方法。
如果使用的是Thread子类 需要重写run方法,
如果使用的是Thread和Runnable,那么调用的是runnable的run方法
public class Thread implements Runnable {
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
原理:FutureTask的底层实现:适配器
FutureTask implements RunnableFuture
RunnableFuture extends Runnable, Future
这就解释了为什么FutureTask可以放在Thread中执行了
new Thread(futureTask, "futureTask").start();
FutureTask#构造方法
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
//runnable适配成了callable
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
//创建了一个RunnableAdapter
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
//发现RunnableAdapter实现了Callable
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
//默认的call方法实现调用的是 runnable的run
public T call() {
task.run();
return result;
}
}
FutureTask#run
FutureTask实现了Runnable接口,并且实现了自己的run方法。
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
//获取本类中的callable属性
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//调用的是callable的call方法
result = c.call();
//执行完设置执行完成标记
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
//重点!!!!
//调用set方法
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
FutureTask#set:修改状态为完成后唤醒
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
LockSupport.unpark(t);在这里将之前阻塞的线程unpark。
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
//在call方法运行期间 即 还没有返回值返回
//这个时候调用get方法获取结果
// LockSupport.park(this);
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
//空实现 扩展方法 模板模式
done();
callable = null; // to reduce footprint
}
FutureTask#get:根据状态:等待还是立刻返回
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING){
s = awaitDone(false, 0L);
}else{
return report(s);
}
}
如果不是完成状态COMPLETING就会调用awaitDone继续Park
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
//如果调用get方法的线程被打断
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
重点来了,这里是一个死循环,然后判断了一个状态,调用了LockSupport.park方法让这个线程阻塞,就是说你get的时候,如果我没执行完,那你就在这等着我执行完。
也就是说其他的线程调用本线程的LockSupport.unpark也不会让本线程退出等待,而是会在下一轮循环继续等待。
比如下面的t1
//先 park 再 unpark:先暂停再继续往下执行
Thread t1 = new Thread(() -> {
//t1在get方法中被Park方法阻塞
futureTask.get();
}
,"t1");
t1.start();
sleep(2);
//即使这里t1被unpark
//也会在死循环中立刻又被Park
LockSupport.unpark(t1);
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}