interrupt
package sun.nio.ch;
public interface Interruptible {
void interrupt(Thread var1);
}
/* The object in which this thread is blocked in an interruptible I/O
* operation, if any. The blocker's interrupt method should be invoked
* after setting this thread's interrupt status.
*/
private volatile Interruptible blocker;
......
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess() checkAccess} method
* of this thread is invoked, which may cause a {@link
* SecurityException} to be thrown.
*
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
* <p> If this thread is blocked in an I/O operation upon an {@link
* java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, the thread's interrupt
* status will be set, and the thread will receive a {@link
* java.nio.channels.ClosedByInterruptException}.
*
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt status will be set and it will return
* immediately from the selection operation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
*
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
* @throws SecurityException
* if the current thread cannot modify this thread
*
* @revised 6.0
* @spec JSR-51
*/
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
/**
* Tests whether the current thread has been interrupted. The
* <i>interrupted status</i> of the thread is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return false (unless the current thread were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if the current thread has been interrupted;
* <code>false</code> otherwise.
* @see #isInterrupted()
* @revised 6.0
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if this thread has been interrupted;
* <code>false</code> otherwise.
* @see #interrupted()
* @revised 6.0
*/
public boolean isInterrupted() {
return isInterrupted(false);
}
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
......
private native void interrupt0();
- 线程中维护了一个volatile变量blocker,为Interruptible类型,这是个接口,只定义了一个interrupt方法。
- 根据thread.interrupt方法的注释,可以看到:
- 除非是线程自己interrupt本身,否则都需要处理抛出的SecurityException异常
- 如果线程是由调用wait、join、sleep导致处于BLOCKED、WAITING、TIME_WAITING状态,此时调用interrupt方法会生效,线程的中断状态会被设置(set interrupt flag),而sleep、wait、join内部会不断检查中断状态,如果检查到中断状态被设置,则会调用blocker.interrupt(thread r)方法,清除中断标记并抛出异常中断线程。
- interrupt只是修改中断状态,而不会直接触发中断。
join
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
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;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
/**
* Waits at most {@code millis} milliseconds plus
* {@code nanos} nanoseconds for this thread to die.
*
* <p> This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @param nanos
* {@code 0-999999} additional nanoseconds to wait
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative, or the value
* of {@code nanos} is not in the range {@code 0-999999}
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
join(millis);
}
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
其实有点儿不太理解这个方法,先上测试代码:
package thread;
public class JoinTest {
public static void main(String[] args) throws InterruptedException {
final long totalStartTime = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("sub thread1");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sub1 spend =" + (System.currentTimeMillis() - totalStartTime));
}
});
Thread thread2 = new Thread(new Runnable() {
public void run() {
System.out.println("sub thread2");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sub2 spend =" + (System.currentTimeMillis() - totalStartTime));
}
});
Thread thread3 = new Thread(new Runnable() {
public void run() {
System.out.println("sub thread3");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sub3 spend =" + (System.currentTimeMillis() - totalStartTime));
}
});
long startTime = System.currentTimeMillis();
System.out.println("before start sub thread,spend time=" + (startTime - totalStartTime));
thread.start();
System.out.println("before join,Current Thread = " + Thread.currentThread().getName() + ",status=" + Thread.currentThread().getState());
thread.join(4000);
System.out.println("after join,Current Thread = " + Thread.currentThread().getName() + ",status=" + Thread.currentThread().getState());
thread2.start();
thread3.start();
System.out.println("total spend = " + (System.currentTimeMillis() - startTime));
}
}
结果:
before start sub thread,spend time=6
before join,Current Thread = main,status=RUNNABLE
sub thread1
sub1 spend =3013
after join,Current Thread = main,status=RUNNABLE
total spend = 3008
sub thread2
sub thread3
sub2 spend =6019
sub3 spend =6019
- 首先,join不是static方法,所以不能像sleep一样,可以直接用类调用,而是只能通过实例进行调用
- 上面测试代码中有4个线程,Main,subThread,subThread2,subThread3
- Main线程是首先启动的,所以会打印结果中行1
- 测试代码第50行启动了线程subThread,可以看到结果中行2和行3,分别是Main和subThread1打印出来的,也就两个线程在并行处理。
- 53行执行subThread.join,可以看到主线程Main和subThread依然在交替执行,主线程Main打印了结果行5及6,subThread打印了行4
- 在56、57行分别开启了线程thread2 和 thread3,可以看到,这两个线程都是在线程thread1执行结束之后才开始的,而且是并行进行。
- 所以join只针对在执行join之后启动的线程,会让这些后续启动的线程都等待当前线程结束。就像上面的注释:Waits at most {@code millis} milliseconds for this thread to die. A timeout of {@code 0} means to wait forever.
- 而已经启动了线程,并不受影响,比如测试代码中的Main Thread、subThread1
参考资料:https://mp.weixin.qq.com/s/pvGOS7jk6UumfzsD9Kyf9A
https://www.jianshu.com/p/6e1ea479e01d