信号量Semaphore实现两个线程的交替运行
作者:互联网
方法来源:https://www.cnblogs.com/misscai/p/14666782.html
其思路: 使用信号量 Semaphore 有加有减,控制并发。
图出自上方链接。
我不理解,两个都是Semapore(1);不可以么?即使不能确定哪个线程先执行,使两个线程交替运行应该是OK的吧。
此时,我对Semaphore的使用还非常不熟悉。
那么验证一下我的猜想。
实现效果:打印1-100的数值,两个线程A B,A线程打印奇数,B线程打印偶数。
public class SemaphoreTest_2 { public static void main(String[] args) { Semaphore s1 = new Semaphore(1); Semaphore s0 = new Semaphore(1); new A_th_Semp_2(s0, s1).start(); new B_th_Semp_2(s0, s1).start(); } } class A_th_Semp_2 extends Thread{ private Semaphore s0; private Semaphore s1; public A_th_Semp_2(Semaphore s0, Semaphore s1){ this.s0 = s0; this.s1 = s1; } @Override public void run() { for (int i = 1 ; i < 100;){ try { s1.acquire(); System.out.println("线程A:==>"+i); i = i + 2; TimeUnit.SECONDS.sleep(1); s0.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class B_th_Semp_2 extends Thread{ private Semaphore s0; private Semaphore s1; public B_th_Semp_2(Semaphore s0, Semaphore s1){ this.s0 = s0; this.s1 = s1; } @Override public void run() { for (int i = 2 ; i <= 100;){ try { s0.acquire(); System.out.println("线程B:==>"+i); i = i + 2; TimeUnit.SECONDS.sleep(1); s1.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:显然不太对,orz。以下只是一种运行结果,实际上有可能是B线程先执行。
线程A:==>1
线程B:==>2
线程A:==>3
线程B:==>4
线程B:==>6
线程A:==>5
线程B:==>8
线程A:==>7
线程A:==>9
线程B:==>10
线程A:==>11
线程B:==>12
线程B:==>14 。。。
分析原因:
* * 失败原因在于:
* B线程一个循环结束即s1 release()之后,进入下一个循环时s0.acquire()有可能直接获取到。
*
* 假设A线程先获取s1后打印A完成,进入sleep();此时 B线程获取了s0打印B完成 进入sleep();
* 线程A sleep()完成之后释放s0,进入下一个循环获取s1失败(此时B 线程还是sleep,没有释放s1),在此处阻塞
* 线程B sleep()完成之后释放s1,进入下一个循环获取s0成功(s0由线程A 释放),直接打印B;后进入sleep();
* 线程A 一直在获取s1,此时获取s1成功,打印 A成功后进入sleep();
* 因此出现了连续打印两个B ,连续打印两个A的情况。
是时候研究一下Semaphore的基本使用了。尝试之后,得出了相关结论:
* 信号量Semaphore使用:
* 假设release()N次,就能直接acquire() (N + permits)次!
* permits也可以为负数,没有限制。当(N+permits)> 0 时,acquire();才会成功。
* eg: new Semaphore(-1); 当 连续release 2次之后,就能acquire();成功 1次。
*
* release()并不会 阻塞线程,只有acquire() 会阻塞线程
也就是 Semaphore(0) 的时候,并不是不可以acquire();,只要release();一次那就可以acquire();一次。
acquire(int N); 其实可以有参数获取N个,其效果相当于连续执行了N次acquire();release(int N);同理。
某种程度上Semaphore也是可以循环使用的,只要release() 之后就可以重新 acquire() 。而CountDownLatch是真正意义上的无法循环使用。
Semaphore(0,-1) 类似于单向运行 ,只有在release();执行N次,(N+permits) > 0之后 才能acquire();成功。
那么要实现交替打印的功能,只要修改调用代码为:
public class SemaphoreTest_2 { public static void main(String[] args) { Semaphore s0 = new Semaphore(0); Semaphore s1 = new Semaphore(1); new A_th_Semp_2(s0, s1).start(); new B_th_Semp_2(s0, s1).start(); } }
运行结果:线程A与B交替打印,程序正常关闭。
。。。
线程A:==>87
线程B:==>88
线程A:==>89
线程B:==>90
线程A:==>91
线程B:==>92
线程A:==>93
线程B:==>94
线程A:==>95
线程B:==>96
线程A:==>97
线程B:==>98
线程A:==>99
线程B:==>100
代码运行流程是:
* 由于s0的permits为0,所以一定是A线程先运行。
* A线程 s1.acquire();成功后执行打印等操作,进入sleep();此时 B线程 s0 依旧acquire();失败
* 当 A线程 休眠完毕,s0.release(); 直接进入下一循环,s1.acquire();时阻塞(由于上个循环acquire();之后并没有release();所以再次 .acquire();时阻塞 )
* 此时 B线程 s0.acquire();成功 (A线程 上个循环释放了s0),执行打印等操作之后,进入sleep;此时 A线程s1.acquire(); 依旧失败,理由同上;
* 当 B线程 休眠完毕,s1.release();直接进入下一循环,s0.acquire();时阻塞。(由于A 线程上个循环的release();被B 线程的上个循环使用了,需等待下一个relase;)
* 此时 A线程 s1.acquire();时成功,继续执行第二个循环的逻辑
* 以此类推
其实整个思路就是利用了Semaphore(0);的单向运行,Semaphore(-1,-2)其实都拥有这个特性,也能实现相应的功能,只是可以但没必要。
具体做法就是修改一下两处代码:
main(xxx);中初始化时s0改为-1;A线程中 s0.release();改为s0.release(2);
Semaphore s0 = new Semaphore(-1);
s0.release(2);
运行效果自行验证。
标签:s0,s1,acquire,信号量,线程,Semaphore,release 来源: https://www.cnblogs.com/CccccNun/p/15798454.html