多线程-关闭与中止

本文探讨了Java中线程的关闭和中断,包括Thread和Runnable的区别、线程安全问题、线程控制方法。重点介绍了如何通过设置标志和interrupt方法来通知线程中断,但不强制关闭,强调了正确处理中断异常以停止线程的重要性,并警告了使用已弃用方法(如stop)的风险。同时提到了volatile在停止线程时的局限性,建议使用interrupt配合判断来实现线程的优雅退出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 线程的关闭和中止!

关于继承Thread 和 Runnable的区别。

首先在现有的多线程方式下,想要进行如售票的例子,若开启三个线程,会出现线程安全问题,Runnable和Thread都会出现超卖现象。

线程中常用的方法:

控制线程的关闭:通过在线程中设置flag,在设置对应的setFlag方法,在main线程中调用子线程的setFlag即可。

package Thread_;

/**
 * @program:多线程和IO
 * @descripton:控制线程的关闭
 * @author:ZhengCheng
 * @create:2021/9/15-10:01
 **/
public class Thread05 {
    public static void main(String[] args) throws InterruptedException {
        //开启线程
        T t = new T();
        t.start();
        Thread.sleep(5000);
        System.out.println("过了五秒");
        //关闭线程
        t.setFlag();
    }
}
class T extends Thread{
    private boolean flag = true;
    int count = 0;
    @Override
    public void run() {
        while (flag){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+(++count));
        }
    }
    public void setFlag (){
        flag = false;
    }
}

控制线程的中断(并非终止线程):使用interupt方法。当线程被interupt过后,会被异常处理机制,catch到interuptedexception,在后续代码块中,执行我们线程被interupt过后的逻辑。

        注意:我们在使用interupt去中断线程时,我们只是去通知该线程,而并非强制其关闭。因为常常使用interupt去中断他人线程的线程,往往是不了解他人线程的业务逻辑的。故线程何时被中止,其实是赋予在了被通知的线程上。那么结合代码,理解就比较轻松。当被通知过后,该线程继续操作,知道interuptException被自己捕获,那么该线程才开始着手(先处理完捕获异常后的代码,如关机时,保存适当的信息等)关闭自身。

   

 
package Thread_;

/**
 * @program:多线程和IO
 * @descripton:控制线程的关闭
 * @author:ZhengCheng
 * @create:2021/9/15-10:01
 **/
public class Thread05 {
    public static void main(String[] args) throws InterruptedException {
        //开启线程
        T t = new T();
        t.start();
        Thread.sleep(5000);
        System.out.println("过了五秒");
        t.interrupt();
    }
}
class T extends Thread{
    private boolean flag = true;
    int count = 0;
    @Override
    public void run() {
        while (flag){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("我被中止了");
                break;
            }
            System.out.println(Thread.currentThread().getName()+(++count));
        }
    } 
}

 while内try/catch的问题?

为什么在上述代码中,仍然使用了break跳出while循环?这要追溯到更深层次的原因?

如何才能正确的停止线程?

package ThreadKN.howtostop;

/**
 * @program:多线程和IO
 * @descripton:
 * @author:ZhengCheng
 * @create:2021/9/16-10:54
 **/
public class mlw2 {
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = () -> {
            int num = 0;
            while (num <= 10000) {
                if (num % 100 == 0) {
                    System.out.println(num);
                }
                num++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    /*System.out.println("我被中止了");*/
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

 在上述的代码中,线程会被中断吗?答案是否定的。原因是在while循环里try/catch了该异常,在try/catch中,我们把异常给吞掉了,如果再catch里没有跳出循环,会继续执行。

那么,如何正确的停止线程,在实际的应用过程中呢?

package ThreadKN.howtostop;

/**
 * @program:多线程和IO
 * @descripton:
 * @author:ZhengCheng
 * @create:2021/9/16-11:17
 **/
public class stopThreadReal implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new stopThreadReal());
        thread.start();
        Thread.sleep(100);
        thread.interrupt();
    }

    @Override
    public void run() {
        while (true){
            System.out.println("执行中");
            /*method(); */      //该方法无法暂停,因为异常被吞掉了

            /*try {
                method1();
            } catch (InterruptedException e) {
                System.out.println("保存日志,然后关闭线程");
                e.printStackTrace();
                break;
            }*/

            //搭配method2使用
            if (!Thread.currentThread().isInterrupted()){
                System.out.println("程序运行结束");
                break;
            }
            method2();
        }
    }
    /*使用该方法可以,因为下一级的方法抛出异常,导致我们此时只有两种选择
        继续抛出异常,或者处理该异常。但是此时run方法的上层,在Runnable中,
         public abstract void run();    该方法是没有抛出异常,所以此时,run就是
         最顶级方法,只能在现在处理该异常。*/
    private void method1() throws InterruptedException {
        Thread.sleep(100);
    }

//改进,补足中断的信息,重新抛出!
    private void method2() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            System.out.println("保存日志,然后关闭线程");
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
    private void method() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            System.out.println("保存日志,然后关闭线程");
            e.printStackTrace();
        }
    }
}

中断的意义:

        中断线程化的意义在于:在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断程将作为内核线运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。
————————————————
原文链接:https://blog.youkuaiyun.com/tiantao2012/article/details/78052202

错误的停止线程的方法:

使用stop,resume,suspend(极易导致死锁)等被弃用的方法。stop()来停止线程时,会导致线程运行一半突然停止,没办法完成一个基本单位的操作,会出现脏数据。

使用volatile来停止线程,看似可行,但是并没有普适性的!举例子,当消费者消费速度慢,而生产者生产速度快时,就会出现这样的情况。

package ThreadKN.howtostop;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;

/**
 * @program:多线程和IO
 * @descripton:当线程陷入阻塞时,volatile是无法停止线程的
 * 当生产者速度快,消费者慢,那么阻塞队列会满,等待消费者进一步消费
 * @author:ZhengCheng
 * @create:2021/9/16-12:10
 **/
public class VolatileCantStop {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Object> storage = new ArrayBlockingQueue<>(10);
        Producer producer = new Producer(storage);
        Consumer consumer = new Consumer(storage);
        Thread pro = new Thread(producer);
        pro.start();

        Thread.sleep(1000);

        while (consumer.needNums()){
            System.out.println(consumer.storage.take()+"被消费者消费");
            Thread.sleep(1000);
        }
        System.out.println("消费者不需要更多数据了");
        //一旦消费者不需要更多数据了,我们应当让生产者停下来
        producer.canceled=true;
    }
}
class Producer implements Runnable{
    BlockingQueue storage;
    public volatile boolean canceled = false;
    public Producer(BlockingQueue storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= Integer.MAX_VALUE/2
            && !canceled){
                if (num % 100 == 0){
                    storage.put(num); //阻塞点,阻塞在此,走不到检查循环体的地方。
                    System.out.println(num);
                }
                num++;
                Thread.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("生产者停止运行");
        }
    }
}
class Consumer {
    BlockingQueue storage;
    public Consumer(BlockingQueue storage) {
        this.storage = storage;
    }
    public boolean needNums(){
        if (Math.random()>0.95){
            return false;
        }
        return true;
    }
}

如上代码,我们在使用volatile时,就导致了其阻塞以后,无法到达while的判断区,从而造成没有 停止该线程。那么如何修复呢?使用Interupt

package ThreadKN.howtostop;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
 * @program:多线程和IO
 * @descripton:
 * @author:ZhengCheng
 * @create:2021/9/16-12:37
 **/
public class InterupCanStop {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Object> storage = new ArrayBlockingQueue<>(10);
        Pro pro = new Pro(storage);
        Con01 con = new Con01(storage);
        Thread thread = new Thread(pro);
        thread.start();
        //等待队列装满
        Thread.sleep(1000);

        while (con.needNums()){
            System.out.println( con.storage.take()+"被消费了");
        }
        System.out.println("消费者不买了");
        //停止pro的线程
        thread.interrupt();
        System.out.println("生产者结束了生产");
    }
}
class Pro implements Runnable{
    BlockingQueue storage;
   //public volatile boolean canceled = false;
    public Pro(BlockingQueue storage) {
        this.storage = storage;
    }
    @Override
    public void run() {
        int num = 0;
        try {
            while (num <= Integer.MAX_VALUE/2){
                if (num % 100 == 0){
                    storage.put(num);
                    System.out.println(num);
                }
                num++;
                Thread.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            System.out.println("生产者停止运行");
        }
    }
}
class Con01 {
    BlockingQueue storage;
    public Con01(BlockingQueue storage) {
        this.storage = storage;
    }
    public boolean needNums(){
        if (Math.random()>0.95){
            return false;
        }
        return true;
    }
}

 深入Interupt的源码:

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(); //native 方法!
}

在interupt中,做出多次判断,最后还是执行的是native方法。有兴趣和基础的可以深入 openJDK中查看核心源码。

 关于Interupt的一些相关方法

1.使用这个静态方法,会自动清除线程的Interupt状态

这个静态方法的作用对象问题:是谁在执行这条语句,他返回的就是谁。!

public static boolean interrupted() {
    return currentThread().isInterrupted(true);//中间的提示为:ClearInterrupted:
}

2.不清除线程的Interupt状态

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

问题:如何正确的停止线程?(展开,多点回答)

如何处理不可中断的阻塞?(例子目前我还不会)

针对特定的情况,使用特定的处理方法

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值