线程的交互及其相关

线程的中断

因为线程中的stop()方法,已经被弃用,因为它是不安全的,使用它相当于你在家用电脑,直接在没有预知的情况下直接给你停电了一样,太暴力,所以不能用它。应该用 interrupt()这个不是直接中断,而是将当前线程标志为中断标记,只是一个标记。怎么用,代码示例如下:

 1 package com.huolongluo.coindemo.thread;
 2 
 3 /**
 4  * Created by 火龙裸 on 2019/11/13.
 5  * desc   : 线程的中断
 6  * version: 1.0
 7  */
 8 public class ThreadDemo {
 9     public static void main(String[] args) {
10         MyThread myThread = new MyThread();
11         myThread.start();//启动子线程
12 
13         try {
14             Thread.sleep(500);//当前工作的那个线程(主线程)沉睡500毫秒(也就是让子线程跑500毫秒钟)
15         } catch (InterruptedException e) {
16             e.printStackTrace();
17         }
18 //        myThread.stop();//不安全,已被弃用
19         myThread.interrupt();//主线程沉睡500毫秒后,继续在主线程当中,通过“myThread”对象,将子线程标记为中断,
20     }
21 }
22 
23 class MyThread extends Thread {
24     @Override
25     public void run() {
26         for (int i = 0; i < 1000000; i++) {
27             //(Thread.interrupted()返回当前线程(这个代码所运行的那个线程)标志位(当前代码所运行在的那个线程状态),并将标志位重置
28             if (Thread.interrupted()) {//也可以使用isInterrupted(),判断当前线程(这个代码所运行的那个线程)标志位,但isInterrupted()不会重置标志位
29                 //收尾工作
30                 return;
31             }
32             System.out.println("number: " + i);
33         }
34     }
35 }

注意:Thread.interrupted()是个静态方法,和直接使用isInterrupted()是有区别的。虽然都会返回当前代码执行的那个线程的线程状态,但是Thread.interrupted()还会同时将标志位重置为false。

另外一种情况,当子线程有沉睡/等待操作,主线程当中通过子线程对象调用了interrupt()方法,将子线程的线程标志为标记为中断,因为子线程有睡眠,所以会先抛出子线程的异常,而不是执行判断Thread.interrupted()判断,这个时候所以收尾工作应该在子线程中的e.printStackTrace();之后,子线程所抛出的InterruptedException异常,也会将自己的线程中断状态标志位重置为false,所以要是没有在子线程的e.printStackTrace()之后做收尾工作,相当于没有中断,仍旧会一直循环执行下去。因为这个时候子线程在休眠,为了避免浪费资源,收尾工作需要在子线程抛异常的那个位置。

代码示例如下:

 1 package com.huolongluo.coindemo.thread;
 2 
 3 /**
 4  * Created by 火龙裸 on 2019/11/13.
 5  * desc   : 线程的中断
 6  * version: 1.0
 7  */
 8 public class ThreadDemo {
 9     public static void main(String[] args) {
10         MyThread myThread = new MyThread();
11         myThread.start();//启动子线程
12 
13         try {
14             Thread.sleep(500);//当前工作的那个线程(主线程)沉睡500毫秒(也就是让子线程跑500毫秒钟)
15         } catch (InterruptedException e) {
16             e.printStackTrace();
17         }
18 //        myThread.stop();//不安全,已被弃用
19         myThread.interrupt();//主线程沉睡500毫秒后,继续在主线程当中,通过“myThread”对象,将子线程标记为中断,
20     }
21 }
22 
23 class MyThread extends Thread {
24     @Override
25     public void run() {
26         for (int i = 0; i < 1000000; i++) {
27             //(Thread.interrupted()返回当前线程(这个代码所运行的那个线程)标志位(当前代码所运行在的那个线程状态),并将标志位重置
28             if (Thread.interrupted()) {//也可以使用isInterrupted(),判断当前线程(这个代码所运行的那个线程)标志位,但isInterrupted()不会重置标志位
29                 System.out.println("判断标志位");
30                 //收尾工作
31                 return;
32             }
33             try {
34                 Thread.sleep(2000);//当前子线程沉睡2秒
35             } catch (InterruptedException e) {//InterruptedException异常,也会将标志位重置
36                 e.printStackTrace();
37                 //收尾工作
38                 System.out.println("子线程 收尾啦");
39                 return;
40             }
41             System.out.println("number: " + i);
42         }
43     }
44 }

运行结果:

还有一种,睡眠方式:SystemClock.sleep(2000),它与Sleep(2000)的区别是,SystemClock.sleep(2000)把InterruptedException异常给吃掉了,它不会抛异常,所以也不会重置标志位,只是单纯的让当前线程睡2秒钟。示例代码如下:

运行结果:

只打印一行,然后再return结束掉了。

线程的等待

线程交互中,也经常会用到等待wait(),但其实wait()方法和notifyAll()方法都是一个Object类中的方法。代码示例如下:

 1 package com.huolongluo.coindemo.thread;
 2 
 3 /**
 4  * Created by 火龙裸 on 2019/11/16.
 5  * desc   : 线程等待wait()
 6  * version: 1.0
 7  */
 8 public class ThreadDemo2 {
 9     public static void main(String[] args) {
10         WaitDemo waitDemo = new WaitDemo();
11         waitDemo.runTest();
12     }
13 }
14 
15 interface TestDemo {
16     void runTest();
17 }
18 
19 class WaitDemo implements TestDemo {
20     private String str;
21 
22     @Override
23     public void runTest() {
24         Thread thread1 = new Thread(new Runnable() {
25             @Override
26             public void run() {
27                 try {
28                     Thread.sleep(1000);
29                 } catch (InterruptedException e) {
30                     e.printStackTrace();
31                 }
32                 initStr();//初始化字符串
33             }
34         });
35         thread1.start();
36 
37         Thread thread2 = new Thread(new Runnable() {
38             @Override
39             public void run() {
40                 try {
41                     Thread.sleep(500);
42                 } catch (InterruptedException e) {
43                     e.printStackTrace();
44                 }
45                 printStr();//打印字符串
46             }
47         });
48         thread2.start();
49     }
50 
51     private synchronized void initStr() {
52         str = "火龙裸";
53         System.out.println("初始化完成");
54         notifyAll();
55     }
56 
57     private synchronized void printStr() {
58         while (str == null) {
59             System.out.println("循环中");
60             try {
61                 wait();//释放锁,进入到等待队列
62             } catch (InterruptedException e) {
63                 e.printStackTrace();
64             }
65         }
66         System.out.println("打印结果: " + str);
67     }
68 }

运行结果:

结果正是我想要的。但是假如在printStr()方法中,去掉wait()方法,其他代码不动,则会出现永远一直打印“循环中”,就算直到1秒后,也不会执行“初始化initStr()方法”。因为那个monitor,锁一直被thread2所持有没被释放。这里面调用wait()方法后,线程是进入到等待队列(先进先出),被唤醒后,依旧排队执行,以队列的形式,排在唤醒(主叫)的那个线程后面。还有wait()方法和notifyAll()方法都是必须要写到synchronized里面,要是没有被synchronized包裹,去执行的时候,会报错,所以必须是这样写。

原文地址:https://www.cnblogs.com/huolongluo/p/11854124.html