哈哈哈,这个问题真的是愧对我敲的每个字母,再次证明了本人还是新手村小绿人。
问题出现场景:研究两个线程间通信,使其交替执行时(这是另外一篇草稿),在演化了生产者/消费者模式之后,
灵机一动,既然只有两个线程交互,那只需要有两个状态标识就可以了,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标志来停止线程,和这个原理相似。话不多说,大刀阔斧开始验证。
设想结果: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关键字。而且一定存在操作标识位的方式停止线程成功的先例。问题到底出现在哪里?
回归理性分析,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标识位来完成两个线程间的交替执行,问题原因已经找到,just so so啦。