一、线程的创建方式
构造函数
public Thread(ThreadGroup group, Runnable target, String name, long stackSize)
参数讲解:
1)target 为启动此线程时调用其 run 方法的对象,如果为 null ,则调用此线程的 run 方法; 2)name 为线程名称;
3)group 为线程所属的线程组 , 如果是 null 并且有一个安全管理器,则该组由 SecurityManager.getThreadGroup() 决定,如果没有安全管理员或SecurityManager.getThreadGroup() 返回 null ,则该组将设置为当前线程的线程组;
4)stackSize 为线程具有的堆栈大小 ,堆栈大小是虚拟机为该线程的堆栈分配的大致的地址空间字节数,stackSize参数的影响(如果有的话)与平台有关。
1、继承 Thread 类
① 定义 Thread 类的子类,并重写该类的 run 方法,该 run 方法的方法体就代表了线程要完成的任务。因此把 run() 方法称为执行体。
② 创建 Thread 子类的实例,即创建了线程对象。
③ 调用线程对象的 start() 方法来启动该线程。
线程特性:
● 线程能被标记为守护线程,也可以是用户线程
● 每个线程均分配一个 name,默认为(Thread-自增数字)的组合
● 每个线程都有优先级(1-10),默认为 5。高优先级线程优先于低优先级线程执行。
● main所在的线程组为main,构造线程的时候没有显示的指定线程组,线程组默认和父线程一样。
● 当线程中的 run() 方法代码里面又创建了一个新的线程对象时,新创建的线程优先级和父线程优先级一样.
● 当且仅当父线程为守护线程时,新创建的线程才会是守护线程。
● 当 JVM 启动时,通常会有唯一的一个非守护线程(这一线程用于调用指定类的 main() 方法)。
// 定义一个继承Thread类的子类,重写Thread的run()方法
class MyThread extends Thread{
@Override
public void run() {
doSomething();
}
private void doSomething() {
System.out.println("我是一个线程中的方法");
}
}
public class NewThread {
public static void main(String[] args) {
// 创建Thread子类的实例,即创建了线程对象
MyThread myThread=new MyThread();
// 调用线程对象的start()方法来启动该线程
myThread.start();
}
}
注:在 main() 方法里创建一个 MyThread 对象,调用该对象的 start() 方法,start() 方法会通过对系统底层的一系列操作,创建出一个相应的线程,与当前线程并发执行。如果直接调用 run() 方法,程序将执行完 run() 方法后才会执行 main() 方法中后面的代码,这样就是单线程执行而不是多线程并发执行了。
2、实现 Runnable 接口
① 定义 runnable 接口的实现类,并重写该接口的 run() 方法,该 run() 方法的方法体同样是该线程的线程执行体。
② 创建 Runnable 实现类的实例,并依此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
③ 调用线程对象的 start() 方法来启动该线程。
// 定义runnable接口的实现类,并重写该接口的run()方法
class RunnableThread implements Runnable{
@Override
public void run() {
doSomeThing();
}
private void doSomeThing() {
System.out.println("我是一个线程方法");
}
}
public class NewThread {
public static void main(String[] args) {
// 创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象
Runnable runnable=new RunnableThread();
Thread thread=new Thread(runnable);
// 调用线程对象的start()方法来启动该线程
thread.start();
}
}
3、使用 Callable 和 Future 创建线程
① 创建 Callable 接口的实现类,
并实现 call() 方法
,该 call() 方法将作为线程执行体,并且有返回值
。
② 创建Callable实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的call()方法的返回值。
③ 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
④调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
。
// Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值
class CallableThread implements Callable<String>{
@Override
public String call() throws Exception {
doSomeThing();
// call()方法必须要有返回值
return "需要返回的值";
}
private void doSomeThing() {
System.out.println("我是线程中的方法");
}
}
public class NewThread {
public static void main(String[] args) {
Callable<String> callable=new CallableThread();
// 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值
FutureTask<String> futureTask=new FutureTask<String>(callable);
// 使用FutureTask对象作为Thread对象的target创建并启动新线程
Thread thread=new Thread(futureTask);
thread.start();
try {
// 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
futureTask.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
以上三种方法的比较
① 采用实现 Runnable、Callable 接口的方式创见多线程时
● 优势
1)线程类只是实现了 Runnable 接口或Callable接口,
还可以继承其他类
。
2)在这种方式下,多个线程可以共享同一个 target 对象,所以非常适合多个相同线程来处理同一份资源的情况,能够与锁机制(synchronized 与 Lock)共同使用实现线程同步
。
3)可以将 CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。● 劣势
编程稍微复杂,如果要访问当前线程,则必须使用 Thread.currentThread() 方法
。
② 用继承 Thread 类的方式创建多线程时
● 优势
编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程
。
● 劣势
线程类已经继承了 Thread 类,所以不能再继承其他父类。
二、线程的常用方法
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
1、非静态方法
(1)start() 与 run()
1)
start() 的作用是启动一个新线程,新线程会执行相应的run()方法
,start()不能被重复调用。
2)run() 就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程
!
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
public void run(){
System.out.println(Thread.currentThread().getName()+" is running");
}
};
public class Demo {
public static void main(String[] args) {
Thread mythread=new MyThread("mythread");
System.out.println(Thread.currentThread().getName()+" call mythread.run()");
mythread.run();
System.out.println(Thread.currentThread().getName()+" call mythread.start()");
mythread.start();
}
}
运行结果:
main call mythread.run()
main is running
main call mythread.start()
mythread is running
可以发现:mythread.run() 是在主线程 main 中调用的,该 run() 方法直接运行在主线程 main 上;mythread.start() 会启动线程 mythread,线程 mythread 启动之后,会调用 run() 方法;此时的 run() 方法是运行在线程 mythread 上。
(2)join():线程插队
join() 的作用是等待该线程结束再执行,这里需要理解的就是该线程是指的
主线程等待子线程的终止才能执行
。
在很多情况下,主线程生成并启动了子线程。如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束。但是如果主线程处理完其他的事务后需要用到子线程的处理结果,那么主线程就需要等待子线程执行完成之后再结束,这个时候就要用到 join() 方法了
。
1)不加 join() 案例:
class Thread1 extends Thread{
private String name;
public Thread1(String name) {
super(name);
this.name=name;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!");
for (int i = 0; i < 5; i++) {
System.out.println("子线程"+name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程运行结束!");
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
运行结果:
可以发现在子线程结束之前,主线程已经结束了。
2)加 join() 案例:
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
try {
mTh1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
mTh2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
运行结果:
可以发现:
主线程一定会等子线程都结束了才结束
。
(3)setPriority()
更改线程的优先级。
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
(4)interrupt()
Thread.interrupt() 方法的作用是中断线程,将会
设置该线程的中断状态位为 true
,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true),它并不像stop方法那样会中断一个正在运行的线程
。
interrupt() 方法只是改变中断状态,不会中断一个正在运行的线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程检查到中断标识,就得以退出阻塞的状态。更确切的说,
如果线程被 Object.wait, Thread.join 和 Thread.sleep 三种方法之一阻塞,此时调用该线程的 interrupt() 方法,那么该线程将抛出一个 InterruptedException中断异常
(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt() 将不起作用,直到执行到 wait()、sleep()、join() 时,才马上会抛出 InterruptedException
。
2、静态方法
(1)yield():线程礼让
yield() 用来暂停当前正在执行的线程对象,并执行其他线程。
yield() 应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程可以获得运行机会
。因此,使用 yield() 的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证 yield() 会让步成功,因为让步的线程还有可能被线程调度程序再次选中
。
注意:yield() 不是线程转到阻塞状态,而是转到可运行状态。
class ThreadYield extends Thread{
public ThreadYield(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("" + this.getName() + "-----" + i);
// 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
if (i ==30) {
Thread.yield();
}
}
}
public static void main(String[] args) {
ThreadYield yt1 = new ThreadYield("张三");
ThreadYield yt2 = new ThreadYield("李四");
yt1.start();
yt2.start();
}
}
运行结果有两种,第一种情况:李四(线程)当执行到30时会CPU时间让掉,这时张三(线程)抢到CPU时间并执行;第二种情况:李四(线程)当执行到30时会CPU时间让掉,这时李四(线程)抢到CPU时间并执行。
(2)sleep() 方法
sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。当线程睡眠时,它睡在某个地方,
在苏醒之前是不会返回到可运行状态
。当睡眠时间到期,则返回到可运行的状态。所以,sleep()方法指定的时间为线程不会运行的最短时间。当线程休眠时间结束后,会返回到可运行状态,注意不是运行状态,如果要到运行状态还需要等待CPU调度执行
。
线程的 sleep() 方法应该写在线程的 run() 方法里,就能让对应的线程睡眠。
class Runner1 implements Runnable{
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 3; i++) {
System.out.println("Runner1 : " + i);
}
}
}
public class Thread1 {
public static void main(String[] args) {
Runner1 r1 = new Runner1();
Thread t = new Thread(r1);
t.start();
for (int i = 0; i < 3; i++) {
System.out.println("main thread :"+i);
}
}
}
结果:
到底是让哪个线程睡眠
sleep() 方法只能让当前线程睡眠。
调用某一个线程类的对象 t.sleep(),睡眠的不是 t,而是 t 所在的当前线程
。class Thread1 extends Thread{ public void run() { for (int i = 0; i < 3; i++) { System.out.println("Runner1 : " + i); } } } public class Thread2 { public static void main(String[] args) { Thread1 t = new Thread1(); t.start(); try { Thread.sleep(5000); // 此处是类名.sleep() System.out.println("当前运行的线程名称: "+ Thread1.currentThread().getName()); } catch(InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 3; i++) { System.out.println("main thread :"+i); } } }
运行结果如下:
Runner1 : 0 Runner1 : 1 Runner1 : 2 --------------------------------- 此处睡眠5秒,5秒后出现以下: 当前运行的线程名称: main main thread :0 main thread :1 main thread :2
代码验证:在 Thread1 的 run() 中不写 sleep(),在主线程中写 Thread1.sleep(5000),结果不是
Thread1 睡眠,还是主线程睡眠。
sleep() 和 yield() 的区别:
(1) sleep() 使当前线程进入阻塞状态,所以执行 sleep() 的线程在指定的时间内肯定不会被执行;
(2)yield() 只是使当前线程重新回到可执行状态,不会进入阻塞状态,所以执行 yield() 的线程有可能在进入到可执行状态后马上又被执行。
(3)实际上,
yield() 方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给此线程,否则,继续运行原来的线程
。 所以yield()方法称为“退让”,它把运行机会让给了同等优先级的其他线程。(4)另外,
sleep 方法允许较低优先级的线程获得运行机会,但 yield() 方法执行时,当前线程仍处在可运行状态,所以,不可能让出较低优先级的线程些时获得 CPU 占有权
。
(5) 在一个运行系统中,如果较高优先级的线程没有调用 sleep 方法,又没有受到 I\O 阻塞,那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。
(3)wait()
wait() 方法是 Object 对象的方法。线程与锁是分不开的,线程的同步、等待、唤醒都与对象锁是密不可分的。
wait() 方法会将当前获得对象锁的线程放入 wait set,主动释放对象所,并放弃锁对象上的所有同步声明,当前线程会因为线程调度的原因处于休眠状态而不可用,等待被唤醒。
自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。只有通过以下四个方法可以主动唤醒:notify()、notifyAll()、Thread.interrupt() 和等待时间过完。
当线程被唤醒后,线程就从wait set 中移除并且重新获得线程调度能力,同时像其它线程一样持有锁对象。需要注意的是 notify() 唤醒调用后,并不是马上就释放对象锁的,而是在相应的 synchronized(){} 语句块执行结束后。
从语法角度来说就是Obj.wait()、Obj.notify必须在synchronized(Obj){…}语句块内
。一段 synchronized 的代码被一个线程执行之前,他要先拿到执行这段代码的权限, 在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。
取到锁后,他就开始执行同步代码(被 synchronized 修饰的代码); 线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。 这样就保证了同步代码在统一时刻只有一个线程在执行。
Thread.sleep() 与 Object.wait() 的区别
Thread.sleep() 与 Object.wait() 二者都可以暂停当前线程,释放 CPU 控制权。
主要的区别在于Object.wait() 在释放 CPU 同时,释放了对象锁的控制
。
三、线程的停止
停止线程是在多线程开发时很重要的技术点,掌握此技术可以对线程的停止进行有效的处理。停止线程在 Java 语言中并不像 break 语句那样干脆,需要一些技巧性的处理。
在 java 中有三种方法可以停止线程:
(1)使用退出标志,让线程正常退出,也就是当run方法执行完之后终止;
一般 run() 方法执行完,线程就会正常结束。然而,常常有些线程是伺服线程,它们需要长时间的运行,只有在外部某些条件满足的情况下,才能关闭这些线程。
可以使用一个 boolean 类型的标志位来控制
循环,并通过设置这个标志为 true 或 false 来控制 while 循环是否退出。
(2)使用 stop 方法强制终止线程,但是不推荐使用,因为 stop 和 suspend 及 resume 一样,是 java 废弃的方法;
程序中可以直接使用thread.stop()来强行终止线程,但是stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,不安全主要是:thread.stop()调用之后,创建子线程的线程就会抛出ThreadDeatherror的错误,并且会释放子线程所持有的所有锁。一般任何进行加锁的代码块,都是为了保护数据的一致性,如果在调用thread.stop()后导致了该线程所持有的所有锁的突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因此,并不推荐使用stop方法来终止线程。
(3)使用 interrupt 方法中断线程(推荐使用)
如果线程调用了sleep()、同步锁的 wait()、socket 中的 receiver() 和 accept() 等方法时,会使线程处于阻塞状态。当调用线程的 interrupt() 方法时,会抛出 InterruptException 异常。阻塞中的那个方法抛出这个异常,我们可以
通过代码捕获该异常,然后 break 跳出循环状态,从而让我们有机会结束这个线程的执行
。通常很多人认为只要调用 interrupt 方法线程就会结束,实际上是错的, 一定要先捕获 InterruptedException 异常之后通过 break 来跳出循环,才能正常结束 run 方法。
1、 使用 interrupt() 方法中断线程
(1)中断线程
线程的 interrupt() 方法是中断线程,将会
设置该线程的中断状态位,即设置为 true
,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。它并不像 stop() 方法那样会中断一个正在运行的线程。
(2)判断线程是否被中断
判断某个线程是否已被发送过中断请求,则使用 Thread.currentThread().isInterrupted() 方法(因为它将线程中断标示位设置为 true 后,不会立刻清除中断标示位,即不会将中断标设置为 false),而不要使用 thread.interrupted()(该方法调用后会将中断标示位清除,即重新设置为 false)方法来判断。
private static void test2() {
Thread thread = new Thread(() -> {
while (true) {
Thread.yield();
// 响应中断
if (Thread.currentThread().isInterrupted()) {
System.out.println("Java技术栈线程被中断,程序退出。");
return;
}
}
});
thread.start();
thread.interrupt();
}
(3)中断线程抛出异常
如果一个线程处于了阻塞状态(如线程调用了 thread.sleep、thread.join、thread.wait 以及可中断的通道上的 I/O 操作方法后可进入阻塞状态),则线程在检查中断标示时如果发现中断标示为true,则会在这些阻塞方法(sleep、join、wait 及可中断的通道上的 I/O 操作方法)调用处抛出 InterruptedException 异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为 false。
抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求,根据自己的需求确认是否可以直接忽略该中断,还是应该马上退出。这样,我们就可以捕捉到中断异常,并根据实际情况对该线程从阻塞方法中异常退出而进行一些处理
。
public void run() {
try {
while (true){
Thread.sleep(1000l);//阻塞状态,线程被调用了interrupte()方法,清除中断标志,抛出InterruptedException
//dosomething
boolean isIn = this.isInterrupted();
//运行状态,线程被调用了interrupte()方法,中断标志被设置为true
//非阻塞状态中进行中断线程操作
if(isIn) break;//退出循环,中断进程
}
}catch (InterruptedException e){
//阻塞状态中进行中断线程操作
boolean isIn = this.isInterrupted();//退出阻塞状态,且中断标志被清除,重新设置为false,所以此处的isIn为false
return;//退出run方法,中断进程
}
}
(4)抛出异常后的处理
没有任何语言方面的需求一个被中断的线程应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断。某些线程非常重要,以至于它们应该不理会中断,而是在处理完抛出的异常之后继续执行,但是更普遍的情况是,一个线程将把中断看作一个终止请求,这种线程的run方法遵循如下形式:
public void run() { try { ... /* * 不管循环里是否调用过线程阻塞的方法如sleep、join、wait,这里还是需要加上 * !Thread.currentThread().isInterrupted()条件,虽然抛出异常后退出了循环,显 * 得用阻塞的情况下是多余的,但如果调用了阻塞方法但没有阻塞时,这样会更安全、更及时。 */ while (!Thread.currentThread().isInterrupted()&& more work to do) { do more work } } catch (InterruptedException e) { // 线程在wait或sleep期间被中断了 } finally { // 线程结束前做一些清理工作 } }
上面是while循环在try块里,如果try在while循环里时,应该在catch块里重新设置一下中断标示,因为抛出InterruptedException异常后,中断标示位会自动清除,此时应该这样:
public void run() { while (!Thread.currentThread().isInterrupted()&& more work to do) { try { ... sleep(delay); } catch (InterruptedException e) { Thread.currentThread().interrupt(); //重新设置中断标示 } } }
1)try - catch 捕捉(不推荐)
sleep 方法抛出 InterruptedException 后,中断标识也被清空置为 false,如果我们在 catch 没有通过调用 interrupt() 方法再次将中断标识置为 true,这就导致程序吞掉了 InterruptedException
,导致上层调用栈获取不到异常信息。
void mySubTask(){
...
try{
sleep(delay);
}catch(InterruptedException e){
}//不要这样做
...
}
2)使用 throws 将异常上抛
不使用try来捕获这样的异常,让方法直接抛出
void mySubTask() throws InterruptedException {
...
sleep(delay);
...
}
3)重新中断(推荐)
有时候抛出 InterruptedException 并不合适,
当一个阻塞方法检测到中断并抛出 InterruptedException 时,它会清除中断状态(中断标志位由true变为false)
。如果捕捉到 InterruptedException 但是不能重新抛出它,那么应该保留中断发生的证据,以便调用栈中更高层的代码能知道中断,并对中断作出响应,这可以通过调用 interrupt() 以 “重新中断” 当前线程来完成
。
class Thread1 extends Thread{
public Thread1(String name) {
super(name);
}
@Override
public void run() {
try {
Thread.sleep(10000); // 阻塞,产生 InterruptedException 异常,清除中断标志位
} catch (InterruptedException e) {
System.out.println("Java技术栈线程休眠被中断,程序退出。");
Thread.currentThread().interrupt(); // 重新置中断
}
while (true) {
// 响应中断
if (Thread.currentThread().isInterrupted()) {
System.out.println("Java技术栈线程被中断,程序退出。");
return;
}
}
}
public static void main(String[] args) throws InterruptedException {
test thread = new test("lhj");
thread.start();
thread.interrupt(); // 中断
}
}
运行结果:
sleep 方法抛出 InterruptedException 后,中断标识也被清空置为 false,如果我们在 catch 中没有通过调用 interrupt() 方法再次将中断标识置为 true,程序会一直循环下去,这就导致程序吞掉了 InterruptedException
。总的来说,我们应该留意 InterruptedException,当我们捕获到该异常时,绝不可以默默的吞掉它,什么也不做,因为这会导致上层调用栈什么异常信息也获取不到。其实在编写程序时,捕获的任何受检异常我们都不应该吞掉。
如果不重新中断:class Thread1 extends Thread{ public Thread1(String name) { super(name); } @Override public void run() { try { Thread.sleep(10000); // 阻塞,产生 InterruptedException 异常,清除中断标志位 } catch (InterruptedException e) { System.out.println("Java技术栈线程休眠被中断,程序退出。"); } while (true) { // 响应中断 if (Thread.currentThread().isInterrupted()) { System.out.println("Java技术栈线程被中断,程序退出。"); return; } } } public static void main(String[] args) throws InterruptedException { test thread = new test("lhj"); thread.start(); thread.interrupt(); // 中断 } }
运行结果:
sleep 方法抛出 InterruptedException 后,中断标识也被清空置为 false,但是由于在catch中没有重新中断,导致程序继续运行,不会停止。