线程中断的方式 interrupt与volatile

Thread.stop()方法

  • 此方法虽然可以立即停止线程,但是是不安全的方法。已被废弃。
  • 如果强制使用此方法停止线程,可能会使一些清理工作得不到完成。另外一个情况就是对锁的对象进行了解锁,导致数据得不到同步的问题。

interrupt()方法

  • 调用此方法会在当前线程中打一个停止的标记,并不会真正的停止线程。
  • 要想真正的停止线程,还需要结合interrupted()isInterrupted()

interrupted | interrupted

  • interrupted: 测试当前线程是否处已经中断
  • isInterrupted: 测试线程是否处已经中断

interrupted的方法声明

publc static boolean interrupted();

isInterrupted的方法声明

publc boolean isInterrupted();

两者的区别

  1. interrupted:判断的是当前线程 this.interrupted()
  2. isInterrupted: 判断的是调用此方法的线程
  3. interrupt() 方法执行后,调用interrupted() | isInterrupted() ,会打印为true(默认为false)。
    false代表线程未中断,true为线程中断。而interrupted()方法具有清除状态的功能,所以如果第二次调用 interrupted() 方法会返回false;

异常法终止线程

即使用interruptedisInterrupted进行判断,满足条件就抛出异常

class MyThread implements Runnable {

    @Override
    public void run() {
        try {
            for (int i=0;i<100;i++){
                if (Thread.currentThread().interrupted()){
                    System.out.println("线程要被终止");
                    throw new InterruptedException();
                }
                System.out.println(i);
            }
            System.out.println("-----"+ "线程终止后我将不再执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("线程终止了");
        }
    }
}

在睡眠中终止线程

如下代码

class Interrupt implements Runnable {
    @Override
    public void run() {
        int sun = 0;
        try {
            while (!Thread.currentThread().isInterrupted() && sun < 10000) {
                if (sun % 100 == 0) {
                    System.out.println(sun);
                }
                sun++;

                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("------------+++++");
        }
 @Test
   public void demo01() throws InterruptedException {
        Runnable runnable = new Interrupt();
        Thread thread = new Thread(runnable);
        thread.start();
        thread.sleep(1000);
        thread.interrupt();
    }

在sleep状态下停止线程,会直接进入clatch语句,并且清除中断状态值,使之变成false
当线程处于wait()时,执行 interrupt() 也会抛出InterruptedException异常。此时无需判断Thread.currentThread().isInterrupted()

return方法终止线程

class s implements Runnable{
    @Override
    public void run() {
        while (Thread.currentThread().isInterrupted()){
            System.out.println("-----"+ "线程终止后我将不再执行");
            return;
        }
    }
}

此方法不推荐使用,建议还是使用异常法,可以在catch块中进行相关的处理

私有堆栈与公有堆栈

线程的私有堆栈与公有堆栈,私有堆栈与公有堆栈中的数值是不同步的
如下代码

class PrintString implements  Runnable{
    private boolean iscont = true;

    public boolean isIscont() {
        return iscont;
    }

    public void setIscont(boolean iscont) {
        this.iscont = iscont;
    }

    @Override
    public void run() {
        System.out.println("进入到了run ···");
        while (iscont){
            System.out.println("---");
        }
        System.out.println("线程被终止了");
    }
    
}

class Run{
    public static void main(String[] args) {
        try {
            PrintString printString = new PrintString();
            Thread thread = new Thread(printString);

            thread.start();
            thread.sleep(1000); 
            
            printString.setIscont(false);
            System.out.println("已经赋值为false");
        }catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

在JVM配置成Server的环境下执行此代码线程并不能停止,原因便是 私有堆栈与公有堆栈中的数值是不同步的。线程一直在私有堆栈中提取 boolean iscont = true; 而 printString.setIscont(false);更新的使公有堆中的数据。所以线程还是会处于死循环中。

volatile

volatile : 关键字的主要作用是使变量在多个线程间可见
使用此特性便可以进行线程的中断

中断线程

class PrintString implements  Runnable{
//此变量的类型
    private volatile boolean iscont = true;
    public boolean isIscont() {
        return iscont;
    }
    public void setIscont(boolean iscont) {
        this.iscont = iscont;
    }
    @Override
    public void run() {
        System.out.println("进入到了run ···");
        while (iscont){
            System.out.println("---");
        }
        System.out.println("线程被终止了");
    }
}

使用此关键字,便可强制线程从公有堆中获取数据

弊端

在线程处于阻塞状态时,此方法便不能够中断线程。
如下代码,生产者进行生产,消费者消费,生产的速度大于消费的速度。若满足生产的数量,则进入阻塞。

class VolatileProducers implements Runnable{

    //BlockingQueue 即阻塞队列
    BlockingQueue  blockingQueue ;
    public static volatile boolean cancel = false;

    Volatileroduction volatileroduction = new Volatileroduction();

    /**  构造方法*/
    public VolatileProducers(BlockingQueue blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        try {
            /** 调用过程未出现阻塞状态的方法 可以正常进行中断操作*/
            //volatileroduction.VolatileProducers1(cancel);

            /** 会产生处于阻塞状态*/
            volatileroduction.VolatileProducers2(cancel,blockingQueue);

            /** 使用interputed中断线程*/
            //volatileroduction.VolatileProducers3(cancel,blockingQueue);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("--"+cancel);
            System.out.println("--"+Thread.interrupted());
        }
    }
}


/**
 * 生产者
 */
class Volatileroduction{

/** 不会产生阻塞的方法*/
    public void VolatileProducers1(boolean cancel) throws InterruptedException {
        int sum=0;
        while (sum < 10000 && !cancel){
            if (sum %100 ==0){
                System.out.println(sum);
            }
            sum++;
            Thread.sleep(10);
        }
    }

    //此方法在线程处于阻塞状态时,使用volatile并不会是线程中断
    public void VolatileProducers2(boolean cancel,BlockingQueue queue) throws InterruptedException {

        int sum=0;
        while (sum < 10000 && !cancel){
            if (sum %100 ==0){
                System.out.println("生产者生产了"+sum);
                //当阻塞队列满的时候,会处于阻塞状态,代码无法往下执行,自然无法进行while判段,也就无法中断线程
                queue.put(sum);
            }
            sum++;
        }
    }

	/**  interrupt() 方法*/
    public void VolatileProducers3(boolean cancel,BlockingQueue queue) throws InterruptedException {

        int sum=0;
        while (sum < 10000 && !Thread.currentThread().isInterrupted()){
            if (sum %100 ==0){
                System.out.println("生产者生产了"+sum);
                queue.put(sum);
            }
            sum++;
        }
    }
}


//消费者
class Consumers{

    BlockingQueue queue;

    public Consumers(BlockingQueue queue) {
        this.queue = queue;
    }

    public boolean needMoreNum(){
        if (Math.random() > 0.95){
            return false;
        }
        return true;
    }

}
//main方法调用
public static void main(String[] args) throws InterruptedException {

        ArrayBlockingQueue queue = new ArrayBlockingQueue(10);
        /** 生产者生产*/
        VolatileProducers producers = new VolatileProducers(queue);
        Thread producersthread = new Thread(producers);
        producersthread.start();
        producersthread.sleep(1000);

        /** 消费者*/
        Consumers consumers = new Consumers(queue);
        while (consumers.needMoreNum()){
            System.out.println(consumers.queue.take()+"被消费了");
            Thread.sleep(100);
        }
        System.out.println("无需消费");

        //将生产者停止生产
        producers.cancel = true;

		/** 中断线程*/
        //producersthread.interrupt();
    }

由以上代码可以看出,中断线程最好还是使用 interrupt()方法

volatile与synchronized 比较

  1. 性能上,前者要比后者好
  2. 范围上,volatile只能修饰变量,synchronized可以方法,代码快登
  3. synchronized会发生阻塞,volatile不会
  4. volatile 可以保证数据可见性,不能保证原子性。synchronized可以保证原子性,也可以间接保证可见性。
  5. 线程安全包含原子性和可见性两个方面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值