信号量Semaphore实现两个线程的交替运行

方法来源:https://www.cnblogs.com/misscai/p/14666782.html

其思路:    使用信号量 Semaphore 有加有减,控制并发。

图出自上方链接。

信号量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);

运行效果自行验证。

上一篇:Matlab中快速去除字符串中的元音字母


下一篇:题解 CF1361B Johnny and Grandmaster