多线程核心知识总结(三)——线程停止,中断总结

   日期:2020-10-03     浏览:157    评论:0    
核心提示:多线程核心知识总结三.线程停止,中断1.讲解原理原理介绍:使用interrupt了来通知,而不是强制使用一个线程来通知另一个线程该停止的机制,只是一种通知,如果该线程本身不决定停止,则其不会停止,被停止线程的本身,更熟悉停止自己需要做那些处理和清理工作,所以正确停止线程,是如何使用interrupt合理通知该线程并让该线程配合停止。2.最佳实践通常线程会在什么情况下停止run方法的所有代码都运行完毕了有异常出现,并且方法中没有捕获正确停止方法:interrupt通常情况下线程会在什

多线程核心知识总结

三.线程停止,中断

1.讲解原理

原理介绍:使用interrupt了来通知,而不是强制
使用一个线程来通知另一个线程该停止的机制,只是一种通知,如果该线程本身不决定停止,则其不会停止,被停止线程的本身,更熟悉停止自己需要做那些处理和清理工作,所以正确停止线程,是如何使用interrupt合理通知该线程并让该线程配合停止。

2.最佳实践

通常线程会在什么情况下停止

  • run方法的所有代码都运行完毕了
  • 有异常出现,并且方法中没有捕获

正确停止方法:interrupt

  • 通常情况下线程会在什么情况下停止

public class RightWayStopThreadWithoutSleep implements Runnable { 

    @Override
    public void run() { 
        int num = 0;
        while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) { 
            if (num % 10000 == 0) { 
                System.out.println(num + "是10000的倍数");
            }
            num++;
        }
        System.out.println("任务运行结束了");
    }

    public static void main(String[] args) throws InterruptedException { 
        Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }
}
  • 线程可能被阻塞

public class RightWayStopThreadWithSleep { 

    public static void main(String[] args) throws InterruptedException { 
        Runnable runnable = () -> { 
            int num = 0;
            try { 
                while (num <= 300 && !Thread.currentThread().isInterrupted()) { 
                    if (num % 100 == 0) { 
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) { 
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(500);
        thread.interrupt();
    }
}
  • 如果线程在每次迭代后都阻塞

public class RightWayStopThreadWithSleepEveryLoop { 
    public static void main(String[] args) throws InterruptedException { 
        Runnable runnable = () -> { 
            int num = 0;
            try { 
                while (num <= 10000) { 
                    if (num % 100 == 0) { 
                        System.out.println(num + "是100的倍数");
                    }
                    num++;
                    Thread.sleep(10);
                }
            } catch (InterruptedException e) { 
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

自动清除中断信号


public class CantInterrupt { 

    public static void main(String[] args) throws InterruptedException { 
        Runnable runnable = () -> { 
            int num = 0;
            while (num <= 10000 && !Thread.currentThread().isInterrupted()) { 
                if (num % 100 == 0) { 
                    System.out.println(num + "是100的倍数");
                }
                num++;
                try { 
                    Thread.sleep(10);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
}

中断线程的两种姿势之优先抛出


public class RightWayStopThreadInProd implements Runnable { 

    @Override
    public void run() { 
        while (true && !Thread.currentThread().isInterrupted()) { 
            System.out.println("go");
            try { 
                throwInMethod();
            } catch (InterruptedException e) { 
                Thread.currentThread().interrupt();
                //保存日志、停止程序
                System.out.println("保存日志");
                e.printStackTrace();
            }
        }
    }

    private void throwInMethod() throws InterruptedException { 
            Thread.sleep(2000);
    }

    public static void main(String[] args) throws InterruptedException { 
        Thread thread = new Thread(new RightWayStopThreadInProd());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

中断线程的两种姿势之恢复中断


public class RightWayStopThreadInProd2 implements Runnable { 

    @Override
    public void run() { 
        while (true) { 
            if (Thread.currentThread().isInterrupted()) { 
                System.out.println("Interrupted,程序运行结束");
                break;
            }
            reInterrupt();
        }
    }

    private void reInterrupt() { 
        try { 
            Thread.sleep(2000);
        } catch (InterruptedException e) { 
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException { 
        Thread thread = new Thread(new RightWayStopThreadInProd2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

总结

  • 优先选择:传递中断,使用throw抛出异常到run方法内集中解决

  • 不想或者无法传递: 恢复中断,在catch中,重新interrupt

  • 不应该屏蔽中断

相应线程中断的方法总结

  • Object.wait()/wait(long)/wait(long,int)
  • Thread.sleep(long)/sleep(long,int)
  • Thread.join()/join(long)/join(long,int)
  • java.util.concurrent.BlockingQueue.take()/put(E)
  • java.util…concurrent.locks.Lock.lockIntrruptibly()
  • java.util.concurrent.CountDownLatch.await()
  • java.util.concurrent.Exchanger.exchange(V)
  • java.nio.channels.Selector相关方法
  • java.nio.channels.InterruptibleChannel相关方法

正确停止线程

错误停止的方法

  • 被弃用的stop,suspend,resume方法

public class StopThread implements Runnable { 

    @Override
    public void run() { 
        //模拟指挥军队:一共有5个连队,每个连队10人,以连队为单位,发放武器弹药,叫到号的士兵前去领取
        for (int i = 0; i < 5; i++) { 
            System.out.println("连队" + i + "开始领取武器");
            for (int j = 0; j < 10; j++) { 
                System.out.println(j);
                try { 
                    Thread.sleep(50);
                } catch (InterruptedException e) { 
                    e.printStackTrace();
                }
            }
            System.out.println("连队"+i+"已经领取完毕");
        }
    }

    public static void main(String[] args) { 
        Thread thread = new Thread(new StopThread());
        thread.start();
        try { 
            Thread.sleep(1000);
        } catch (InterruptedException e) { 
            e.printStackTrace();
        }
        thread.stop();
    }
}
  • 用volatile设置boolean标记位

public class WrongWayVolatile implements Runnable { 

    private volatile boolean canceled = false;

    @Override
    public void run() { 
        int num = 0;
        try { 
            while (num <= 100000 && !canceled) { 
                if (num % 100 == 0) { 
                    System.out.println(num + "是100的倍数。");
                }
                num++;
                Thread.sleep(1);
            }
        } catch (InterruptedException e) { 
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException { 
        WrongWayVolatile r = new WrongWayVolatile();
        Thread thread = new Thread(r);
        thread.start();
        Thread.sleep(5000);
        r.canceled = true;
    }
}


public class WrongWayVolatileCantStop { 

    public static void main(String[] args) throws InterruptedException { 
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);

        Producer producer = new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);

        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreNums()) { 
            System.out.println(consumer.storage.take()+"被消费了");
            Thread.sleep(100);
        }
        System.out.println("消费者不需要更多数据了。");

        //一旦消费不需要更多数据了,我们应该让生产者也停下来,但是实际情况
        producer.canceled=true;
        System.out.println(producer.canceled);
    }
}

class Producer implements Runnable { 

    public volatile boolean canceled = false;

    BlockingQueue storage;

    public Producer(BlockingQueue storage) { 
        this.storage = storage;
    }


    @Override
    public void run() { 
        int num = 0;
        try { 
            while (num <= 100000 && !canceled) { 
                if (num % 100 == 0) { 
                    storage.put(num);
                    System.out.println(num + "是100的倍数,被放到仓库中了。");
                }
                num++;
            }
        } catch (InterruptedException e) { 
            e.printStackTrace();
        } finally { 
            System.out.println("生产者结束运行");
        }
    }
}

class Consumer { 

    BlockingQueue storage;

    public Consumer(BlockingQueue storage) { 
        this.storage = storage;
    }

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

public class WrongWayVolatileFixed { 

    public static void main(String[] args) throws InterruptedException { 
        WrongWayVolatileFixed body = new WrongWayVolatileFixed();
        ArrayBlockingQueue storage = new ArrayBlockingQueue(10);

        Producer producer = body.new Producer(storage);
        Thread producerThread = new Thread(producer);
        producerThread.start();
        Thread.sleep(1000);

        Consumer consumer = body.new Consumer(storage);
        while (consumer.needMoreNums()) { 
            System.out.println(consumer.storage.take() + "被消费了");
            Thread.sleep(100);
        }
        System.out.println("消费者不需要更多数据了。");


        producerThread.interrupt();
    }


    class Producer implements Runnable { 

        BlockingQueue storage;

        public Producer(BlockingQueue storage) { 
            this.storage = storage;
        }


        @Override
        public void run() { 
            int num = 0;
            try { 
                while (num <= 100000 && !Thread.currentThread().isInterrupted()) { 
                    if (num % 100 == 0) { 
                        storage.put(num);
                        System.out.println(num + "是100的倍数,被放到仓库中了。");
                    }
                    num++;
                }
            } catch (InterruptedException e) { 
                e.printStackTrace();
            } finally { 
                System.out.println("生产者结束运行");
            }
        }
    }

    class Consumer { 

        BlockingQueue storage;

        public Consumer(BlockingQueue storage) { 
            this.storage = storage;
        }

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

停止线程相关重要函数解析

  • 判断是否已经被中断的方法

    • static boolean interrupted()
      返回之后线程中断状态设为false,会清除中断状态
    • boolean isInterrupted()
      返回线程中断状态,但不清除中断状态

public class RightWayInterrupted { 

    public static void main(String[] args) throws InterruptedException { 

        Thread threadOne = new Thread(new Runnable() { 
            @Override
            public void run() { 
                for (; ; ) { 
                }
            }
        });

        // 启动线程
        threadOne.start();
        //设置中断标志
        threadOne.interrupt();
        //获取中断标志
        System.out.println("isInterrupted: " + threadOne.isInterrupted());
        //获取中断标志并重置
        System.out.println("isInterrupted: " + threadOne.interrupted());
        //获取中断标志并重直
        System.out.println("isInterrupted: " + Thread.interrupted());
        //获取中断标志
        System.out.println("isInterrupted: " + threadOne.isInterrupted());
        threadOne.join();
        System.out.println("Main thread is over.");
    }
}

最终结果为

isInterrupted: true
isInterrupted: false
isInterrupted: false
isInterrupted: true

常见面试问题

1.如何停止线程:
1.原理:用interrupt来请求,好处(保证数据安全,把中断权限交给被中断线程)
2.想停止线程,要请求方,被停止方,子方法被调用方法相互配合
3.最后再说错误的方法:stop/suspend被废弃,volatile的boolean无法处理长时间阻塞的状态
2.如何处理不可中断的阻塞
1.不存在通用的解决方案,针对特定情况,需要去找可以相应中断的方法

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服