Thread的使用二

Thread的使用一中,我们讲解了线程的创建,使用及线程池,这节我们来看看当线程使用完之后,我们改如何终止线程。
终止线程有下面三种方式:

一,调用stop()方法

Java APIThread提供了一个stop()方法,但是这个方法已经被废弃了,因为stop()方法太过暴力了,它会将执行一半的线程强行终止,这样就不会保证线程资源的正确释放,通常是没有给线程正确释放资源的机会,因此会导致程序工作在不确定的状态下。
看下面的这个代码示例:

public class ThreadStopClass {
    private Book book = new Book();

    public static void main(String[] args) throws InterruptedException {
        ThreadStopClass threadStopClass = new ThreadStopClass();

        new Thread(threadStopClass.new ReadThread()).start();

        for (int i = 0; i < 10; i++) {
            Thread write = new Thread(threadStopClass.new writeThread());
            write.start();
            Thread.sleep(150);
            write.stop();
        }
    }

    private class ReadThread implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100000; i++) {
                synchronized (ThreadStopClass.class) {
                    if (book.getBookId() != Integer.parseInt(book.getBookName()))
                        System.out.println(book.toString());
                }

                Thread.yield();
            }
        }
    }

    private class writeThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 100000; i++) {
                synchronized (ThreadStopClass.class) {
                    int value = (int) (System.currentTimeMillis() / 1000);
                    book.setBookId(value);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    book.setBookName(String.valueOf(value));
                }

                Thread.yield();
            }
        }
    }
}

Book类代码:

public class Book {
    private int bookId;
    private String bookName;

    public Book(){
        this(0, "0");
    }

    public Book(int bookId, String bookName){
        this.bookId = bookId;
        this.bookName = bookName;
    }

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    @Override
    public String toString() {
        return String.format("Book[bookID:%d, bookName:%s]", getBookId(), getBookName());
    }
}

运行后结果为:

Book[bookID:1536118381, bookName:1536118380]

出现了结果不一致的情况,如果没有出现,就多运行几次。这就是使用write.stop();强行终止线程造成的数据不一致情况。

二,使用boolean型变量进行终止

更改上面例子中的WriteThread线程类,如下:

    private class writeThread implements Runnable {
        private boolean stopFlag = false;

        private void stopMe(){
            stopFlag = true;
        }

        @Override
        public void run() {
            while (!stopFlag){
                synchronized (ThreadStopClass.class) {
                    int value = (int) (System.currentTimeMillis() / 1000);
                    book.setBookId(value);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    book.setBookName(String.valueOf(value));
                }

                Thread.yield();
            }
        }
    }

主线程main方法中调用更改为:

        for (int i = 0; i < 100; i++) {
            writeThread writeThread = threadStopClass.new writeThread();
            Thread write = new Thread(writeThread);
            write.start();
            Thread.sleep(150);
            writeThread.stopMe();
        }

运行代码后结果上面都没有输出,多运行几次也是一样,说明没有造成数据不一致。

三,使用中断使线程退出

中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了个招呼,其他线程通过调用该线程的interrupt()方法对其进行中断操作。

线程通过检查自身是否被中断来进行响应,线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。如果该线程已经处于终结状态,即使该线程被中断过,在调用该线程对象的isInterrupted()时依旧会返回false。

从Java的API中可以看到,许多声明抛出InterruptedException的方法(例如Thread.sleep(longmillis)方法,当线程在sleep()休眠时,如果被中断,这个异常就会产生)。这些方法在抛出InterruptedException之前,Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException,此时调用isInterrupted()方法将会返回false。

更改上面例子中的WriteThread线程类,如下:

    private class writeThread implements Runnable {

        @Override
        public void run() {
            while (true){
                if (Thread.currentThread().isInterrupted()){
                    System.out.println("The Thread has Interrupted...");
                    break;
                }
                synchronized (ThreadStopClass.class) {
                    int value = (int) (System.currentTimeMillis() / 1000);
                    book.setBookId(value);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        System.out.println("The Thread Interrupted when sleep...");
                        // Thread.sleep()方法由于中断抛出异常。
                        // Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException,
                        // 因为在发生InterruptedException异常的时候,会清除中断标记
                        // 如果不加处理,那么下一次循环开始的时候,就无法捕获这个异常。
                        // 故在异常处理中,再次设置中断标记位

                        Thread.currentThread().interrupt();
                    }
                    book.setBookName(String.valueOf(value));
                }

                Thread.yield();
            }
        }
    }

主线程main方法中调用更改为:

        for (int i = 0; i < 100; i++) {
            writeThread1 writeThread = threadStopClass.new writeThread1();
            Thread write = new Thread(writeThread);
            write.start();
            Thread.sleep(150);
            write.interrupt();
        }

运行结果为:

The Thread Interrupted when sleep...
The Thread has Interrupted...
...
The Thread Interrupted when sleep...
The Thread has Interrupted...

多运行几次也是一样的,并没有造成数据的不一致。
在看下面的例子:

public class DaemonThreadClass {

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

        BusyThread busyThread = new BusyThread();
        busyThread.setDaemon(true);//此方法要放在'start()'方法之前,表示将此线程设置为守护线程,也就是后台线程;只有当JVM中所有的线程都为后台线程时,当前JVM就会退出,那么所有的守护线程也就结束了。

        SleepThread sleepThread = new SleepThread();
        sleepThread.setDaemon(true);

        sleepThread.start();
        busyThread.start();

        Thread.sleep(20);
        busyThread.interrupt();
        sleepThread.interrupt();

        System.out.println("BusyThread interrupt is "+ busyThread.isInterrupted());
        System.out.println("SleepThread interrupt is "+ sleepThread.isInterrupted());

        Thread.sleep(2);
    }

    private static class BusyThread extends Thread{
        @Override
        public void run() {
            while (true){
                if (this.isInterrupted())
                    break;
                System.out.println("Thread "+ Thread.currentThread().getName() +" is busying...");
            }
        }
    }

    private static class SleepThread extends Thread{
        @Override
        public void run() {
            while (true){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    System.out.println("SleepThread Interrupted when sleep...");
                }
            }
        }
    }
}

运行结果为:

Thread Thread-0 is busying...
Thread Thread-0 is busying...
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.example.DaemonThreadClass$SleepThread.run(DaemonThreadClass.java:47)
BusyThread interrupt is true
SleepThread interrupt is false
SleepThread Interrupted when sleep...

可以看出是SleepThread线程抛出异常InterruptedException,其标识位被清除了,而BusyThread并没有被清除。

更改上面例子中的SleepThread类:

    private static class SleepThread extends Thread{
        @Override
        public void run() {
            while (true){
                System.out.println("SleepThread before "+ this.isInterrupted());
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("SleepThread Interrupted when sleep...");
                    System.out.println("SleepThread end "+ this.isInterrupted());
                    this.interrupt();
                }
            }
        }
    }

在这个类中使用了方法sleep,运行后抛出异常InterruptedException,此时中断状态已经被清除了,那么调用方法isInterrupted 的返回值为false;在try{}catch(){}中使用了this.interrupt();这个方法将重新设置了中断状态,那么调用方法isInterrupted 的返回值就为true,表明设置了中断状态。
运行结果:

SleepThread before true
BusyThread interrupt is true
SleepThread interrupt is false
SleepThread Interrupted when sleep...
SleepThread end false

更改例子中的BusyThread类,如下:

    private static class BusyThread extends Thread{
        @Override
        public void run() {
            while (true){
                System.out.println("BusyThread before "+ this.isInterrupted());
                if (this.isInterrupted()){
                    System.out.println("BusyThread Thread.interrupted "+ Thread.interrupted());
                    break;
                }
                System.out.println("BusyThread end2 "+ this.isInterrupted());
            }
        }
    }

在确定线程被中断的判定if语句中,调用了Thread.interrupted()方法。运行结果:

BusyThread before false
BusyThread Thread.interrupted true
BusyThread interrupt is false

那么在不调用Thread.interrupted()这个方法时,运行结果为:

BusyThread before true
BusyThread interrupt is true

interrupt()是用来设置中断状态的。返回true说明中断状态被设置了而不是被清除了。注意:线程中断仅仅是设置线程的中断状态位,不会停止线程。我们调用sleep、wait等此类可中断(throw InterruptedException)方法时,一旦方法抛出InterruptedException,当前调用该方法的线程的中断状态就会被jvm自动清除了,就是说我们调用该线程的isInterrupted 方法时是返回false。如果你想保持中断状态,可以再次调用interrupt方法设置中断状态。这样做的原因是,java的中断并不是真正的中断线程,而只设置标志位(中断位)来通知用户。如果你捕获到中断异常,说明当前线程已经被中断,不需要继续保持中断位。
interrupted是静态方法,查看当前中断信号是true还是false并且清除中断信号,顾名思义interrupted为已经处理中断信号。例如,如果当前线程被中断(没有抛出中断异常,否则中断状态就会被清除),你调用interrupted方法,第一次会返回true。然后,当前线程的中断状态被方法内部清除了。第二次调用时就会返回false。如果你刚开始一直调用isInterrupted,则会一直返回true,除非中间线程的中断状态被其他操作清除了。

上一篇:Thread的使用一

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容