在说明之前大家先看一下start()方法的源码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
JDK中的native方法的实现全部封装到dll中了,这也是c/c++代码常用的手段。这样我们可以猜测一下start0方法中为我们做了什么东西,咱们可以看到group.add(this);以及group.threadStartFailed(this);这两段代码,没有对创建一个新的线程以及改变线程状态进行处理以及调用了run方法。所以猜测到start0方法中应该为我们实现了新建一个线程和将当前线程的状态改为就绪状态和调用run方法。
现在根据以上的分析继续分析一个线程调用两次start方法会出现什么状况,可以说在线程没有接触到start0方法时,执行的所有代码都是同步的。即使是同时两次调用的这个start方法,在start方法中也会有一段代码是同步执行的,第一个代码执行到start0方法后,第一个start方法新创建一个线程,由于看不到start0中的代码。所以我们猜测两种情况:
- start0方法中先创建的线程,然后再修改当前线程的状态
- start0方法中先修改的当前线程的状态,然后再创建个线程
根据这两种情况我们继续往下推:第一种情况,第一次调用start方法先创建线程,后改变状态,这样第二次调用也许会逃避掉第一行对其进行的状态判断,但是,在group.add(this);方法中的第一行还是一个状态判断(这里大家可以看一下ThreadGroup类的add方法),这个也许是真的逃不掉了,因为threadStatus这个标志位是volatile修饰的,即使新建出线程也会在第二次调用start方法时正确的确定当前线程的状态,会抛出IllegalThreadStateException异常。第二种情况,先改变状态,在进行创建新的线程。这样第二次调用直接在第一行那里就会被抛出异常。总之,无论start0方法怎么中创建线程和修改状态的顺序先后,在外侧的代码中进行过滤后都不会允许多次调用start方法的。