您当前的位置: 首页 > 

Dongguo丶

暂无认证

  • 2浏览

    0关注

    472博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

线程中断机制interrupt

Dongguo丶 发布时间:2021-09-20 09:56:17 ,浏览量:2

线程中断机制

相关方法:

    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();
    }

    public static boolean interrupted(){
        return currentThread().isInterrupted(true);
    }

    public boolean isInterrupted(){
        return isInterrupted(false);
    }
什么是中断

首先 一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。 所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。

其次 在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。 因此,Java提供了一种用于停止线程的机制——中断。

中断只是一种协商机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。 若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true; 接着你需要自己写代码不断地检测当前线程的标识位,如果为true,表示别的线程要求这条线程中断, 此时究竟该做什么需要你自己写代码实现。

每个线程对象中都有一个标识,用于表示线程是否被中断;该标识位为true表示中断,为false表示未中断; 通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。

中断的相关API方法

public void interrupt() 实例方法, 实例方法interrupt()仅仅是设置线程的中断状态为true,不会停止线程

public static boolean interrupted() 静态方法,

Thread.interrupted(); 判断线程是否被中断,并清除当前中断状态 这个方法做了两件事: 1 返回当前线程的中断状态 2 将当前线程的中断状态设为false

这个方法有点不好理解,因为连续调用两次的结果可能不一样。

当第一次调用时,中断状态如果为true,会清除中断状态,也就是将中断状态设为false

第二次调用时,中断状态就为false

public boolean isInterrupted() 实例方法, 判断当前线程是否被中断(通过检查中断标志位)

如何使用中断标识停止线程 1Jdk1.0有stop()、suspend()、resume()的控制方法,但由于潜在的死锁威胁因此在后序的JDK中被弃用。

反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。

suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。

对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

2使用共享变量的方式 (volatile中断标志—状态标志变量)

在这种方式中,之所以引入volatile共享变量,是因为该变量可以被多个执行相同任务的线程用来作为是否中断的信号,通知中断线程的执行。

package com.dongguo.interrupt;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/9/6 0006-12:14
 * @description: 通过一个volatile变量实现停止线程
 */
public class InterruptDemo {
    private static volatile boolean isStop = false;

    public static void main(String[] args) {

        new Thread(() -> {
            while (true) {
                if (isStop) {
                    // isStop = true  执行线程中断相关逻辑
                    System.out.println("isStop = true 程序结束");
                    break;
                }
                System.out.println("isStop = false 程序正常运行");
            }
        }, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            isStop = true;
        }, "t2").start();
    }
}
运行结果
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = false 程序正常运行
 isStop = true 程序结束

t1线程正常运行,一秒后t2线程将isStop设置为true

t1线程结束,停止线程

3atomic包下的原子包装类AtomicBoolean
package com.dongguo.interrupt;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Dongguo
 * @date 2021/9/6 0006-12:14
 * @description: 通过AtomicBoolean实现停止线程
 */
public class InterruptDemo {

    static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                if (atomicBoolean.get()) {
                    // isStop = true  执行线程中断相关逻辑
                    System.out.println("atomicBoolean is true 程序结束");
                    break;
                }
                System.out.println("atomicBoolean is false 程序正常运行");
            }
        }, "t1").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            atomicBoolean.set(true);
        }, "t2").start();
    }
}
运行结果:
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is false 程序正常运行
atomicBoolean is true 程序结束

t1线程正常运行,一秒后t2线程将atomicBoolean设置为true

t1线程结束,停止线程

4interrupt

调用interrupt()方法仅仅是在当前线程中打了一个停止的标记,并不是真正立刻停止线程。

package com.dongguo.interrupt;

import java.util.concurrent.TimeUnit;


/**
 * @author Dongguo
 * @date 2021/9/6 0006-12:14
 * @description: 通过interrupt实现停止线程
 */
public class InterruptDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    // isInterrupted = true  执行线程中断相关逻辑
                    System.out.println("isInterrupted is true 程序结束");
                    break;
                }
                System.out.println("isInterrupted is false 程序正常运行");
            }
        }, "t1");
        t1.start();
        System.out.println("---------------" + t1.isInterrupted());
        try {
            TimeUnit.MILLISECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.interrupt();
        System.out.println("---------------" + t1.isInterrupted());
    }
}
运行结果:
---------------false
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
...
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
---------------true
isInterrupted is true 程序结束

t1线程正常运行,5毫秒后主线程将t1线程interrupt,此时t1的中断状态为true

t1调用isInterrupted 判断线程中断状态为true,停止线程

①如果线程处于正常活动状态,那么会将该线程的中断标志设置为true 被设置中断标志的线程将继续运行,不受影响,所以, interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。

也就是说主线程无法将t1线程中断,是否中断由t1线程自己做决定

证明实例方法interrupt()仅仅是设置线程的中断状态位设置为true,不会停止线程

package com.dongguo.interrupt;

import java.util.concurrent.TimeUnit;


/**
 * @author Dongguo
 * @date 2021/9/6 0006-12:14
 * @description: 通过interrupt实现停止线程
 */
public class InterruptDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i=0;i {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.interrupt();
    }
}
运行结果
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.dongguo.interrupt.InterruptDemo.lambda$main$0(InterruptDemo.java:15)
	at java.lang.Thread.run(Thread.java:748)

该程序只是单纯的sleep了5秒中,抛出异常就被结束了

如果是在循环中sleep会是什么情况呢?

③注意:如果线程处于正常活动状态,那么会将该线程的中断标志设置为true 被设置中断标志的线程将继续运行

如果此时在循环中调用sleep方法会将中断状态清除并抛出一个InterruptedException异常。

但是程序不会结束, 需要在捕获异常时再次调用interrupt方法才能立即中断

package com.dongguo.interrupt;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/9/6 0006-13:32
 * @description:
 */
public class InterruptDemo1 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    // isInterrupted = true  执行线程中断相关逻辑
                    System.out.println("isInterrupted is true 程序结束");
                    break;
                }
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                    System.out.println("isInterrupted is false 程序正常运行");
                } catch (InterruptedException e) {
                    //Thread.currentThread().interrupt();
                    e.printStackTrace();
                }      
            }
        }, "t1");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.interrupt();
    }
}

运行结果

image-20210906141302084

因为在循环中调用sleep方法会将中断状态清除并抛出一个InterruptedException异常。

此时t1的中断状态是false 所以才会一直打印isInterrupted is false 程序正常运行

(两阶段终止模式)

问题:在while循环中

当线程正常运行时,被Interrupte打断,while循环下次判断能够判断中断状态为true,可以选择退出循环

但是当线程处于sleep阻塞状态时,被Interrupte打断,会抛出中断异常并且清除中断状态,while循环下次判断能够判断中断状态为false,就不会选择退出循环,而是仍然继续运行while循环。

解决: 需要在捕获异常时再次调用interrupt方法才能立即中断

image-20210911114001787

package com.dongguo.interrupt;

import java.util.concurrent.TimeUnit;

/**
 * @author Dongguo
 * @date 2021/9/6 0006-13:32
 * @description:
 */
public class InterruptDemo1 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    // isInterrupted = true  执行线程中断相关逻辑
                    System.out.println("isInterrupted is true 程序结束");
                    break;
                }
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                    System.out.println("isInterrupted is false 程序正常运行");
                } catch (InterruptedException e) {
                    //在捕获异常时再次调用interrupt方法才能立即中断,否则程序不会停止,也就是中断不会被打断
                    System.out.println("再次调用interrupt方法打断");
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                }

            }
        }, "t1");
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.interrupt();
    }
}
运行结果
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
isInterrupted is false 程序正常运行
再次调用interrupt方法打断
isInterrupted is true 程序结束
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at java.lang.Thread.sleep(Thread.java:340)
	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
	at com.dongguo.interrupt.InterruptDemo1.lambda$main$0(InterruptDemo1.java:20)
	at java.lang.Thread.run(Thread.java:748)
源码分析 interrupt()

interrupt()是Thread类中的一个实例方法

/**
     * Interrupts this thread.
     *
     * 

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. * *

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}. * *

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}. * *

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. * *

If none of the previous conditions hold then this thread's interrupt * status will be set.

* *

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(); }

interrupt()中调用了interrupt0()设置中断标志,interrupt0()是native方法

    private native void interrupt0();
isInterrupted()

isInterrupted()是Thread类中的一个实例方法,调用native的isInterrupted(false) 不清除中断状态

public boolean isInterrupted() {
    return isInterrupted(false);
}

private native boolean isInterrupted(boolean ClearInterrupted);
Interrupted()

静态方法Thread.interrupted()

判断线程是否被中断,并清除当前中断状态,类似i++ 这个方法做了两件事: 1 返回当前线程的中断状态 2 将当前线程的中断状态设为false

调用interrupt()后

第一次调用interrupted() 返回true并清除中断状态

如果调用第二次interrupted (),返回flase

除非两次调用interrupted ()之间再次被interrupt()

package com.dongguo.interrupt;

/**
 * @author Dongguo
 * @date 2021/9/6 0006-14:27
 * @description:
 */
public class InterruptDemo3 {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());
        System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());
        System.out.println("111111");
        Thread.currentThread().interrupt();
        System.out.println("222222");
        System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());
        System.out.println(Thread.currentThread().getName()+"---"+Thread.interrupted());
    }
}
运行结果:
main---false
main---false
111111
222222
main---true
main---false

Thread类中interrupted() 返回currentThread().isInterrupted(true);

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

ClearInterrupted为true

private native boolean isInterrupted(boolean ClearInterrupted);
Java中interrupted和isInterrupted方法的区别?

判断线程是否是停止状态:

  1. interrupted():Thread.interrupted() 是静态方法:内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态(getandset)

测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,

  1. isInterrupted():thread.isInterrupted()

测试调用该方法的线程 thread对象 是否被中断,不清除中断状态,

关注
打赏
1638062488
查看更多评论
立即登录/注册

微信扫码登录

0.0374s