论错误使用boolean,无效结束线程(无效设置线程结束标识)

哈哈哈,这个问题真的是愧对我敲的每个字母,再次证明了本人还是新手村小绿人。

问题出现场景:研究两个线程间通信,使其交替执行时(这是另外一篇草稿),在演化了生产者/消费者模式之后,

灵机一动,既然只有两个线程交互,那只需要有两个状态标识就可以了,boolean不是正满足需求么?

于是大手一挥,写下了绝世代码。

这里有个小技巧:线程执行完成设置的标志值必须让该线程下个循环进入await,这样才能让本线程执行完一次之后放弃锁,其他线程竞争锁获得执行权

虽然代码很垃圾,但是技巧还是适用的。

 1 /**
 2  * @program:
 3  * @description:错误的线程间通信实例
 4  *
 5  * lock;singal
 6  */
 7 public class ThreadCommunicate_3 {
 8 
 9     public static void main(String[] args) {
10         ReentrantLock lock = new ReentrantLock();
11         Condition condition = lock.newCondition();
12         Boolean flag = Boolean.FALSE;
13         Thread a_Th = new A_Th_3(lock, condition, flag);
14         Thread b_Th = new B_Th_3(lock, condition, flag);
15 
16         a_Th.start();
17         b_Th.start();
18     }
19 
20 }
21 /**
22  * 打印奇数线程
23  */
24 class A_Th_3 extends Thread {
25 
26     private final ReentrantLock lock;
27 
28     private final Condition condition;
29 
30     private Boolean flag;
31 
32     public A_Th_3(ReentrantLock lock,Condition condition,Boolean flag) {
33         this.lock = lock;
34         this.condition = condition;
35         this.flag = flag;
36     }
37 
38     @Override
39     public void run() {
40         for (int i = 1; i < 100; ){
41              lock.lock();
42              //true就等着
43               while (flag){
44                   try {
45                       condition.await();
46                   } catch (InterruptedException e) {
47                       e.printStackTrace();
48                   }
49               }
50             System.out.println("线程A:==>" + i);
51             i = i + 2;
52             flag = true;
53             condition.signalAll();
54             lock.unlock();
55         }
56     }
57 }
58 
59 /**
60  * 打印偶数线程
61  */
62 class B_Th_3 extends Thread{
63 
64     private final ReentrantLock lock;
65 
66     private final Condition condition;
67 
68     private Boolean flag ;
69 
70     public B_Th_3(ReentrantLock lock,Condition condition, Boolean flag) {
71         this.lock = lock;
72         this.condition = condition;
73         this.flag = flag;
74     }
75 
76     @Override
77     public void run() {
78         for (int i = 2; i < 101; ){
79             lock.lock();
80             //false就等着
81             System.out.println(flag);
82             while (!flag){
83                 try {
84                     condition.await();
85                 } catch (InterruptedException e) {
86                     e.printStackTrace();
87                 }
88             }
89             System.out.println("线程B:==>" + i);
90             i = i + 2;
91             flag = false;
92             condition.signalAll();
93             lock.unlock();
94         }
95     }
96 }

粗看觉得毫无问题,flag默认值为false,

假设奇数线程先执行,打印完成之后将flag置为true,再次循环时进入while{}...await();偶数线程获取到flag为true,打印完成之后将false置为false,再次循环之后进入while{}...await();

假设偶数线程先执行,自行分析。。。。

然而,运行结果让给大跌眼镜,怎会如此??

现象:只打印一个奇数线程,程序一直不停止。

然而使用arrayList.size() 作为线程通信flag 时完全没有问题。

一番研究之后,发现偶数线程的标识位根本不会因为奇数线程的修改而改变,当然此时我还没有顿悟。

论错误使用boolean,无效结束线程(无效设置线程结束标识)

 

以上只是问题的来源,有点废话,正文由此开始

===============================    =================================================

突然想到之前使用boolean标志来停止线程,和这个原理相似。话不多说,大刀阔斧开始验证。

设想结果:A线程启动之后循环打印,5秒之后B线程将循环标识位设为false,A线程结束循环停止打印,整个程序结束。

 1 public class ThreadCommu_byBoolean {
 2 
 3     public static void main(String[] args) {
 4         boolean flag = true;
 5         Thread a_Th = new A_Th_bool(flag);
 6         Thread b_Th = new B_Th_bool(flag);
 7         a_Th.start();
 8 
 9         try {
10             Thread.sleep(5000);
11         } catch (InterruptedException e) {
12             e.printStackTrace();
13         }
14         b_Th.start();
15     }
16 }
17 
18 class A_Th_bool extends Thread{
19 
20     private volatile boolean flag;
21 
22     public A_Th_bool(boolean flag) {
23         this.flag = flag;
24     }
25     
26     @Override
27     public void run() {
28         while (flag){
29             System.out.println("=======xxx======");
30             try {
31                 Thread.sleep(1000);
32             } catch (InterruptedException e) {
33                 e.printStackTrace();
34             }
35         }
36     }
37 }
38 
39 /**
40  * 设置标识位为false
41  */
42 class B_Th_bool extends Thread{
43 
44     private volatile boolean flag;
45 
46     public B_Th_bool(boolean flag) {
47         this.flag = flag;
48     }
49 
50     @Override
51     public void run() {
52         flag = false;
53      }
54 }

 

事与愿违好事多磨,运行结果与记忆中大相径庭。

现象:A线程一直循环打印,这就意味着B线程设置的标识位A线程根本没有感受到。

奇怪,明明使用了volatile关键字。而且一定存在操作标识位的方式停止线程成功的先例。问题到底出现在哪里?

论错误使用boolean,无效结束线程(无效设置线程结束标识)

 

回归理性分析,arrayList.size();作为标识位就可以成功实现线程的通信,换成了boolen之后就无法达到要求,这两个有什么差别?

说时迟那时快,一指刹那间我悟了,他们之间最根本的差别就是:List是引用类型的对象,而boolean是基本类型;

这就涉及到了基础中的基础___java的传参机制:值传递

此处需恶补知识,哈哈哈哈。

 

此刻 我大概已经摸清了使用boolean传参 命中注定失败的原因,但是仍旧没有一丝线索,该如何正确使用boolean标识位来结束线程。

求索道路不能停止,是时候灵活使用搜索引擎了。

 事例来源:https://www.cnblogs.com/shudonghe/p/3318117.html

 1 volatile boolean shutdownRequested;
 2 
 3 ...
 4 
 5 public void shutdown() { 
 6     shutdownRequested = true; 
 7 }
 8 
 9 public void doWork() { 
10     while (!shutdownRequested) { 
11         // do stuff
12     }
13 }

 

观察片刻,我发现了哗点,外界会调用此class的.shutDown();操作的是此类的内部属性shutdownRequested!!

一般来说,A类的标识符置false操作会在A类内部方法里操作,没有必要在B类中操作A类的属性。(偶写的垃圾代码,QAQ)

由于错误代码使用的是传参,值传递时基本类型的修改只在本方法内部体生效,

因此如果想要使用boolean标识来实现两个线程间的交互,就必须操作各个线程内部属性flag变量。

想要使用boolean标识来停止某个线程,就必须操作该线程的属性flag。

 1 public class ThreadCommu_byBoolean {
 2 
 3     public static void main(String[] args) {
 4         boolean flag = true;
 5         A_Th_bool_1 a_Th_1 = new A_Th_bool_1(flag);
 6         Thread b_Th_1 = new B_Th_bool_1(a_Th_1);
 7 
 8         a_Th_1.start();
 9         try {
10             Thread.sleep(5000);
11         } catch (InterruptedException e) {
12             e.printStackTrace();
13         }
14         b_Th_1.start();
15     }
16 }
17 
18 class A_Th_bool_1 extends Thread{
19 
20     private volatile boolean flag;
21 
22     public A_Th_bool_1(boolean flag) {
23         this.flag = flag;
24     }
25 
26     public boolean isFlag() {
27         return flag;
28     }
29 
30     public void setFlag(boolean flag) {
31         this.flag = flag;
32     }
33 
34     @Override
35     public void run() {
36          while (flag){
37              System.out.println("=======xxx======");
38              try {
39                  Thread.sleep(1000);
40              } catch (InterruptedException e) {
41                  e.printStackTrace();
42              }
43          }
44     }
45 }
46 
47 /**
48  * 设置线程A的结束位标识
49  */
50 class B_Th_bool_1 extends Thread{
51 
52     A_Th_bool_1 a_th_bool;
53 
54     public B_Th_bool_1(A_Th_bool_1 a_th_bool) {
55         this.a_th_bool = a_th_bool;
56     }
57     
58     public void setFlag() {
59         a_th_bool.setFlag(false);
60     }
61 
62     @Override
63     public void run() {
64         setFlag();
65     }
66 }

如我所料,大功告成。

 

论错误使用boolean,无效结束线程(无效设置线程结束标识)

 

至于刚开始的问题,使用boolean标识位来完成两个线程间的交替执行,问题原因已经找到,just so so啦。

 

上一篇:IO测试工具之fio详解


下一篇:SpringBoot2,2021最新阿里Java面试流程