1.3 停止线程

在Java中有三种方式可以终止正在运行的线程:

1-1

  • 使用退出标志,令线程正常退出,也就是当run方法完成后线程正常终止
  • /*有时候run方法永远不会结束,在下面代码中我们定义了一个exit退出标志*/
    /*
    在定义exit时,使用了关键字volatile,目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值
    */
    public
    class ThreadFlag extends Thread { public volatile boolean exit = false; public void run() { while (!exit); } public static void main(String[] args) throws Exception { ThreadFlag thread = new ThreadFlag(); thread.start(); sleep(5000); // 主线程延迟5秒 thread.exit = true; // 终止线程thread thread.join(); System.out.println("线程退出!"); } }
  • 使用Thread.interrupt()方法中断线程,但是interrupt()方法并不是真正地停止线程,而是在当前线程中打了一个停止的标记
  • 使用Thread.stop()方法强行终止线程,这个方法是不安全的(强制让线程停止有可能使一些清理性工作得不到完成;还有就是对锁定的对象进行“解锁”,导致数据得不到同步的处理,出现数据不一致的问题)。已经被弃用作废了。

1-2

  在Java的SDK中,Thread.java类提供了两种方法。

  1. this.interrupted():测试当前线程是否已经中断,当前线程指的是运行这条指令的线程
  2. this.isInterrupted():测试线程是否已经中断
public class t4 {
    public static void main(String[] args){
        try{
            Mthread thread = new Mthread();
            thread.start();
            Thread.sleep(1000);
            thread.interrupt();        
            System.out.println("是否停止1? = " + thread.interrupted());    //停止的是当前进程,当前进行时main
            System.out.println("是否停止2? = " + thread.interrupted());            
        }catch (InterruptedException e){
            System.out.println("main catch!");
            e.printStackTrace();
        }
        System.out.println("end!");    
    }
}

class Mthread extends Thread {
    @Override
    public void run() {
        super.run();
        for(int i = 1; i < 50000; i++) {
            System.out.println("i = " + (i + 1));
        }
    }
}
...
i = 49993
i = 49994
i = 49995
i = 49996
i = 49997
i = 49998
i = 49999
i = 50000
是否停止1? = false
是否停止2? = false
end!

从控制台来看线程并未停止,因为Thread.interrupted()测试的是当前线程是否已经中断,当前线程是main,它从未中断,所以打印出false。

public class t4 {
    public static void main(String[] args) {
        System.out.println("当前进程是:" + Thread.currentThread().getName());
        Thread.currentThread().interrupt();
        System.out.println("是否停止1? = " + Thread.interrupted());    
        System.out.println("是否停止2? = " + Thread.interrupted());            
        System.out.println("end!");
    }
}
当前进程是:main
是否停止1? = true
是否停止2? = false
end!

 

 

观察上述结果,当前进程main的确被停止了,但是为什么第二个布尔值是false呢?以下是官方文档对interrupted方法的解释:

如果连续两次调用该方法,第一次调用后会清除中断状态,所以第二次输出false

  • this.interrupted(): 测试当前线程是否已经是中断状态,并且调用后清除中断状态
  • this.isInterrupted():测试线程Thread对象是否已经是中断状态,但不清除中断状态

1-3 用异常法停止线程

由之前的学习我们知道,Thread.interrupt()并不是真正地停止线程,而是清除中断状态,所有我们可以用捕捉异常的方法来停止线程

 

public class t5 {
    public static void main(String[] args) {
        try{
            AThread thread = new AThread();
            thread.start();
            Thread.sleep(2000);
            thread.interrupt();
        }catch(InterruptedException e){
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

class AThread extends Thread{
    @Override
    public void run() {
        super.run();
        try {
            for(int i = 0; i < 50000000; i++) {
                if(this.interrupted()) {
                    System.out.println("已经是停止状态了!我要退出了!");
                    throw new InterruptedException();
                }
                System.out.println("i = " + (i + 1));
            }
            System.out.println("我在for循环的下面");
        } catch (InterruptedException e) {
            System.out.println("进入AyThread.java类run方法中的catch了!");
            e.printStackTrace();
        }
    }
}

 

i = 770367
i = 770368
i = 770369
i = 770370
i = 770371
i = 770372
i = 770373
i = 770374
i = 770375
i = 770376
i = 770377
i = 770378
i = 770379
end!
已经是停止状态了!我要退出了!
进入AyThread.java类run方法中的catch了!
java.lang.InterruptedException
	at JavaMultiThread.AThread.run(t5.java:26)

 当然还可以将方法interrupt()与return结合使用也能实现停止线程的效果。

public class t6 {
    public static void main(String[] args) throws InterruptedException {    
        XThread thread = new XThread();        
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();            
        //System.out.println("end!");
    }
}

class XThread extends Thread{
    @Override
    public void run() {
        super.run();
        while(true) {
            if(this.isInterrupted()) {
                System.out.println("停止了!");
                return ;
            }
            System.out.println("timer = " + System.currentTimeMillis());
        }
    }
}
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
timer = 1602493631139
停止了!

 

但是不建议这么做,用异常处理更方便一些,因为catch块还可以将异常向上抛,使得线程停止的事件得以传播。

 

1-4

package JavaMultiThread;

public class t6 {
    public static void main(String[] args) {    
        XThread thread = new XThread();        
        thread.start();
        //Thread.sleep(200);
        thread.interrupt();            
        System.out.println("end!");
    }
}

class XThread extends Thread{
    @Override
    public void run() {
        super.run();
        try {
                System.out.println("run begin!");
                Thread.sleep(200000);
                System.out.println("run end");
        } catch (InterruptedException e) {
            System.out.println("在沉睡中被停止!进入catch!" + this.isInterrupted());
            e.printStackTrace();
        }
    }
}
end!
run begin!
在沉睡中被停止!进入catch!false
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at JavaMultiThread.XThread.run(t6.java:19)

 从结果看,如果在sleep状态下停止某一线程,会进入catch语句,并且要清除状态位,变成false。

 

1-5  暂停线程

线程暂停以后还可以恢复运行,在Java多线程中,可以使用suspend()和resume()方法回复线程的执行。

package JavaMultiThread;

public class t4 {
    public static void main(String[] args) {
        try{
            Mthread thread = new Mthread();
            thread.start();
            Thread.sleep(5000);
            
            //A段
            thread.suspend();    
            System.out.println("A =  " + System.currentTimeMillis() + " i = " + thread.getI());    
            Thread.sleep(5000);
            System.out.println("A =  " + System.currentTimeMillis() + " i = " + thread.getI());    
            
            //B段
            thread.resume();
            Thread.sleep(5000);
            
            //C段
            thread.suspend();    
            System.out.println("B =  " + System.currentTimeMillis() + " i = " + thread.getI());    
            Thread.sleep(5000);
            System.out.println("B =  " + System.currentTimeMillis() + " i = " + thread.getI());    
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class Mthread extends Thread {
    private long i = 0;
    public long getI() {
        return i;
    }
    
    public void setI(long i) {
        this.i = i;
    }
    
    @Override
    public void run() {
        while(true) {
            i++;
        }
    }
}

 

A =  1602505229215 i = 3752119017
A =  1602505234224 i = 3752119017
B =  1602505239227 i = 7510113112
B =  1602505244240 i = 7510113112

 可以看到前后两次输出A段的信息相同,这说明线程暂停了;但是B段的信息和A段不同,说明线程又重新恢复了。

1-5-1 suspend与resume方法的缺点-----独占

package JavaMultiThread;

public class t4 {
    public static void main(String[] args) {
        try{
            final SynchronizedObject object = new SynchronizedObject();
            Thread thread = new Thread() {
                @Override
                public void run() {
                    object.printString();
                }
            };
            
            thread.setName("a");
            thread.start();
            Thread.sleep(1000);
            
            Thread thread1 = new Thread() {
                @Override
                public void run() {
                    System.out.println("thread1启动了,但是进入不了printString()方法,只打印1个begin");
                    System.out.println("因为printString()方法被a线程锁定了并且永远suspend暂停了");
                    object.printString();
                }
            };
            thread1.start();
            
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class SynchronizedObject{
    synchronized public void printString() {
        System.out.println("begin!");
        if(Thread.currentThread().getName().equals("a")) {
            System.out.println("进程a永远suspend了!");
            Thread.currentThread().suspend();
        }
        System.out.println("end");
    }
}
begin!
进程a永远suspend了!
thread1启动了,但是进入不了printString()方法,只打印1个begin
因为printString()方法被a线程锁定了并且永远suspend暂停了

  这说明suspend和resume方法使用不当,造成公共的同步对象被独占,导致其他线程无法访问公共同步对象。现在suspend方法已经作废了

1-5-2 suspend与resume方法的缺点------不同步

package JavaMultiThread;

public class t5 {
    public static void main(String[] args) throws InterruptedException {
        final MyObject myobject = new MyObject();
        
        Thread thread = new Thread() {
            @Override
            public void run() {
                myobject.setValue("a", "aa");
            }
        };
        
        thread.setName("a");
        thread.start();
        Thread.sleep(500);
        
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                myobject.printUsernamePassword();
            }
        };
        
        thread1.start();
        
    }
}

class MyObject    {
    private String username = "1";
    private String password = "11";
    
    public void setValue(String u, String p) {
        this.username = u;        
        
        if(Thread.currentThread().getName().equals("a")) {
            System.out.println("停止a线程!");
            Thread.currentThread().suspend();
        }
        this.password = p;

    }
    public void printUsernamePassword() {
        System.out.println(username + " " + password);
    }
}

 

停止a线程!
a 11

  从结果来看,thread线程设置username的值后暂停,并没有给password赋值。thread1读取了此过程中的值,导致读取只赋值到一半的值,出现不同步问题。

从上述两个例子我们可以看出,suspend的resumen方法的缺陷----数据不同步、独占。所以这也是他们过时不被使用的原因。

 

1.6 yield()方法

yield()方法的作用是放弃当前的CPU资源,让给其他的任务去占用CPU执行时间。当时放弃的时间不确定,有可能刚刚放弃,就马上又获得CPU时间片。

 

1.7 线程的优先级

  在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是说CPU优先执行优先级较高的线程对象中的任务。设置线程优先级有助于“线程规划器”确定下一次选择哪一个线程来执行。设置线程优先级用setPriority()方法。

  在Java中,线程的优先级分为1~10折10个等级,如果小于1或者大于10,则JDK会抛出throw new IllegalArgumentException()。默认的优先级是5。

线程的优先级具有一些特性:

①、继承特性:如线程A启动线程B,那么线程B和线程A的优先级是一样的。

package JavaMultiThread;

public class t5 {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("man thread begin priority = " + Thread.currentThread().getPriority());
        
        Thread.currentThread().setPriority(7);        
        MyThread1 thread1 = new MyThread1();
        thread1.start();
        System.out.println("man thread end priority = " + Thread.currentThread().getPriority());
    }
}

class MyThread1 extends    Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("MyThread1 run priority = " + this.getPriority());
        MyThread2 thread = new MyThread2();
        thread.start();
    }
}



class MyThread2 extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println("MyThread2 run priority = " + this.getPriority());
    }
}
man thread begin priority = 5
man thread end priority = 7
MyThread1 run priority = 7
MyThread2 run priority = 7

②、线程优先级具有规则性

所谓规则性指的是,CPU尽量将执行资源让给优先级较高的线程,所以高优先级的线程总是大部分先执行完,但这不代表高优先级的线程全部先执行完总之一句话:县城的优先级与代码的执行顺序无关。

package JavaMultiThread;

import java.util.Random;

public class t5 {
    public static void main(String[] args) throws InterruptedException {
        for(int i = 0; i < 10; i++) {
            MyThread1 thread1 = new MyThread1();
            thread1.setPriority(2);
            thread1.start();
            MyThread2 thread2 = new MyThread2();
            thread2.setPriority(10);
            thread2.start();
        }
    }
}

class MyThread1 extends    Thread{
    @Override
    public void run() {
        super.run();
        long beginTime = System.currentTimeMillis();
        long addResult = 0;
        
        for(int j = 0; j < 10; j++) {
            for(int i = 0; i < 50000; i++) {
                Random random = new Random();
                random.nextInt();
                addResult += i;
            }
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("++++++++ thread 1 use time = " + (endTime - beginTime));

    }
}



class MyThread2 extends Thread{
    @Override
    public void run() {
        super.run();
        long beginTime = System.currentTimeMillis();
        long addResult = 0;
        
        for(int j = 0; j < 10; j++) {
            for(int i = 0; i < 50000; i++) {
                Random random = new Random();
                random.nextInt();
                addResult += i;
            }
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("-------- thread 2 use time = " + (endTime - beginTime));
        
    }
}

 

-------- thread 2 use time = 502
-------- thread 2 use time = 531
-------- thread 2 use time = 682
-------- thread 2 use time = 731
-------- thread 2 use time = 752
-------- thread 2 use time = 786
++++++++ thread 1 use time = 910
-------- thread 2 use time = 912
-------- thread 2 use time = 929
-------- thread 2 use time = 939
-------- thread 2 use time = 987
++++++++ thread 1 use time = 1107
++++++++ thread 1 use time = 1108
++++++++ thread 1 use time = 1128
++++++++ thread 1 use time = 1152
++++++++ thread 1 use time = 1155
++++++++ thread 1 use time = 1158
++++++++ thread 1 use time = 922
++++++++ thread 1 use time = 1170
++++++++ thread 1 use time = 1172

  

③、线程优先级具有随机性。

不要把线程的优先级与运行结果的顺序作为衡量的标准,因为线程较高的线程并不一定都先执行完run方法中的任务,也就是线程优先级与打印顺序无关,他们之间的关系具有不确定性和随机性。

1-8 守护线程

Java线程中有两种线程,一种是用户线程,另一种是守护线程。当进程中不存在非守护线程了,守护线程会自动销毁,如垃圾回收器GC就是守护线程。

package JavaMultiThread;

public class t6 {
    public static void main(String[] args) throws InterruptedException {    
        XThread thread = new XThread();
        thread.setDaemon(true);
        thread.start();
        Thread.sleep(7000);    //让守护线程Daemon Thread多打印一些
        System.out.println(Thread.currentThread().getName() +"离开, thread对象也不再打印了,也就是停止了!");
    }
}

class XThread extends Thread{
    private int i = 0;
    @Override
    public void run() {
        super.run();
        try {
            while(true) {
                i++;
                System.out.println("i = " + (i + 1));            
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
main离开, thread对象也不再打印了,也就是停止了!

  

 

posted @ 2020-10-11 17:57  Peterxiazhen  阅读(157)  评论(0编辑  收藏  举报