sleep() 线程休眠
-
使当前线程休眠,但不会释放对象的同步锁;
-
运行状态 -> 休眠(阻塞状态);
-
线程休眠的时间大于指定的休眠时间就会重新被唤醒,阻塞状态 -> 就绪状态,从而等待CPU的调度执行;
yield() 线程让步
-
立即中止当前线程的执行,重新争夺CPU,但不会释放对象的同步锁(用在同步代码块中就没意义了);
-
运行状态 -> 就绪状态;
-
以便让其他具有相同优先级的等待线程获取执行权,也可能当前线又进入运行状态;
join() 线程合并
-
将子线程的执行和父线程合并;
-
父线程等待子线程执行结束才会继续执行;
下面是join()方法的源码:
/**
* 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);
}
第一句就表明,执行此方法,会等待此线程死亡,即是父线程等待子线程的死亡,或者被中断。
下面是未使用线程合并的程序:
public class Demo {
public static void main(String[] args) {
ChildThread childThread = new ChildThread();
childThread.start();
System.out.println("主线程执行结束");
}
}
class ChildThread extends Thread {
@Override
public void run() {
System.out.println("子线程运行开始");
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程运行结束");
}
}
其运行结果:
主线程执行结束
子线程运行开始
子线程运行结束
可以看出主线程和子线程是并行执行的。
加入线程合并:
ChildThread childThread = new ChildThread();
childThread.start();
try {
System.out.println("并入主线程之前");
childThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行结束");
其运行结果:
并入主线程之前
子线程运行开始
子线程运行结束
主线程执行结束
最初,子线程和主线程都确实是并行执行的,但当主线程执行到childThread.join()
,如果子线程还未结束,则会等待子线程结束,然后才会继续执行主线程。
interrupt() 线程中断
stop()方法和suspend()也可用于线程中断,但这两个方法已在1.2版本被废弃了。
中止处于阻塞状态的线程
当线程由于被调用了sleep()、wait()、join()、等方法而进入阻塞状态;若此时调用线程的interrput()线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除(设为false),同时产生一个InterruptedException异常。通过捕获该异常来中断阻塞线程
@Override
public void run() {
try {
while (true) {
// 执行任务...
}
} catch (InterruptedException ie) {
// 由于产生InterruptedException异常,退出while(true)循环,线程终止!
}
}
@Override
public void run() {
while (true) {
try {
// 执行任务...
} catch (InterruptedException ie) {
// InterruptedException在while(true)循环体内。
// 当线程产生了InterruptedException异常时,while(true)仍能继续运行!需要手动退出
break;
}
}
}
中止处于运行状态的线程
- 中断标记:
@Override
public void run() {
while (!isInterrupted()) {
// 执行任务...
}
}
调用interrrupt()会使isInterrupted()返回true,常用来中断处于阻塞状态的线程。
- 额外添加标记:
@Override
public void run() {
try {
while (true) {
// 执行任务...
}
} catch (InterruptedException ie) {
// 由于产生InterruptedException异常,退出while(true)循环,线程终止!
}
}
综合中断线程阻塞状态和运行状态的方式,以下是比较通用的线程中断方式:
@Override
public void run() {
try {
// 1. isInterrupted()保证,只要中断标记为true就终止线程。
while (!isInterrupted()) {
// 执行任务...
}
} catch (InterruptedException ie) {
// 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。
}
}
实例:
public class Demo {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.start();
// 确保子线程先处于阻塞状态
Thread.sleep(1000);
System.out.println("中断子线程");
myThread.interrupt();
}
}
class MyThread extends Thread {
@Override
public void run() {
try {
while (!isInterrupted()) {
System.out.println("子线程阻塞");
sleep(4000);
}
} catch (InterruptedException e) {
System.out.println("子线程捕获到中断异常");
}
}
}
执行结果:
子线程阻塞
中断子线程
子线程捕获到中断异常
如果把try{}catch(){}放在while内的执行结果:
子线程阻塞
中断子线程
子线程捕获到中断异常
子线程阻塞
子线程阻塞
子线程阻塞
......
结果造成了死循环。
interrupted()和isInterrupted()
相同点:两者都能用于检测对象的中断标记
不同点:
-
interupted()除了返回中断标记,还会清除中断标记(将中断标记设为false);
-
isInterrupted()仅仅返回中断标记;