线程的关闭和中止!
关于继承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); }
问题:如何正确的停止线程?(展开,多点回答)
如何处理不可中断的阻塞?(例子目前我还不会)
针对特定的情况,使用特定的处理方法