线程的关闭和中止!
关于继承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);
}
问题:如何正确的停止线程?(展开,多点回答)
如何处理不可中断的阻塞?(例子目前我还不会)
针对特定的情况,使用特定的处理方法
本文探讨了Java中线程的关闭和中断,包括Thread和Runnable的区别、线程安全问题、线程控制方法。重点介绍了如何通过设置标志和interrupt方法来通知线程中断,但不强制关闭,强调了正确处理中断异常以停止线程的重要性,并警告了使用已弃用方法(如stop)的风险。同时提到了volatile在停止线程时的局限性,建议使用interrupt配合判断来实现线程的优雅退出。
2283

被折叠的 条评论
为什么被折叠?



