美文网首页
Thread的使用二

Thread的使用二

作者: 小和尚恋红尘 | 来源:发表于2018-09-06 14:13 被阅读0次

    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的使用一

    相关文章

      网友评论

          本文标题:Thread的使用二

          本文链接:https://www.haomeiwen.com/subject/edbnwftx.html