线程阻塞(二),Semaphore介绍

上一篇介绍了CountDownLatch和CyclicBarrier的使用,本篇介绍下另外一个阻塞工具Semaphore,有点类似CountDownLatch和CyclicBarrier,它也能实现线程阻塞,也能计数,但是更像是一种线程调度,具体看一下使用方法吧。

构造方法

    public Semaphore(int permits) {//permits代表最多允许多少个线程同时访问
        sync = new NonfairSync(permits);
    }
    public Semaphore(int permits, boolean fair) {// fail表示是否公平,先等待的优先
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

它有几个主要的方法

public void acquire() throws InterruptedException {  }     //获取一个许可
public void acquire(int permits) throws InterruptedException { }    //获取permits个许可
public void release() { }          //释放一个许可
public void release(int permits) { }    //释放permits个许可

使用的时候,先调用acquire()获取许可,如果当前没有可用的许可,则一直等待,直到有可用的许可。

1、简单用法

比如笔者现在到饭点了,假如说公司食堂有5个座位,公司有8个人,这样的话同时最多支持5个人就餐,那剩下的人只能等着,有人吃完饭后,让出座位,让其他人吃饭。

public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(5);// 总共有5个座位
        for (int i = 0; i < 8; i++) {// 总共有8个人需要吃饭
            People people = new People(semaphore, i);
            people.start();
        }
    }

    static class People extends Thread {
        Semaphore semaphore;
        int id;

        public People(Semaphore semaphore, int id) {
            this.semaphore = semaphore;
            this.id = id;
        }

        @Override
        public void run() {
            try {
                this.semaphore.acquire();
                System.out.println("have dinner begin " + id);
                sleep((long) (Math.random() * 5000));//模拟线程耗时各不不同
                System.out.println("have dinner end ****" + id);
                this.semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

执行结果:

have dinner begin 0
have dinner begin 3
have dinner begin 4
have dinner begin 2
have dinner begin 1
have dinner end ****0
have dinner begin 5
have dinner end ****1
have dinner begin 6
have dinner end ****3
have dinner begin 7
have dinner end ****6
have dinner end ****4
have dinner end ****2
have dinner end ****7
have dinner end ****5

这样,只要有线程让出资源,立马就有线程补上去。

2、需要多个资源

当调用acquire(int permits)方法,permits>1的时候,就是需要一次占用多个资源,比如上面的demo,如果是线程内部是这样:

                this.semaphore.acquire(5);
                System.out.println("have dinner begin " + id);
                sleep((long) (Math.random() * 5000));// 模拟线程耗时各不不同
                System.out.println("have dinner end ****" + id);
                this.semaphore.release(5);

执行结果:

have dinner begin 0
have dinner end ****0
have dinner begin 1
have dinner end ****1
have dinner begin 2
have dinner end ****2
have dinner begin 3
have dinner end ****3
have dinner begin 4
have dinner end ****4
have dinner begin 5
...

可能有的人比较胖,一次占了5个座位(餐厅座位是有多小啊),这样一次只能有一个人吃饭了。

3、acquire(int permits)和release(int permits)不一样

3.1 release个数大于acquire个数

                this.semaphore.acquire(1);
                System.out.println("have dinner begin " + id);
                sleep((long) (Math.random() * 5000));// 模拟线程耗时各不不同
                System.out.println("have dinner end ****" + id);
                this.semaphore.release(3);
                System.out.println("可用的座位数:" + this.semaphore.availablePermits());

执行结果:

have dinner begin 0
have dinner begin 3
have dinner begin 2
have dinner begin 1
have dinner begin 4
have dinner end ****0
可用的座位数:3
have dinner begin 5
have dinner begin 7
have dinner begin 6
have dinner end ****3
可用的座位数:3
have dinner end ****5
可用的座位数:6
have dinner end ****1
可用的座位数:9
have dinner end ****4
可用的座位数:12
have dinner end ****7
可用的座位数:15
have dinner end ****2
可用的座位数:18
have dinner end ****6
可用的座位数:21

最终又扩容了,本来是8个座位,最后每个人吃完饭走的时候,释放了自己的座位,又多创造了2个座位,最终变成21个座位了
3.2 release个数小于acquire个数

                this.semaphore.acquire(3);
                System.out.println("have dinner begin " + id);
                sleep((long) (Math.random() * 5000));// 模拟线程耗时各不不同
                System.out.println("have dinner end ****" + id);
                this.semaphore.release(1);
                System.out.println("可用的座位数:" + this.semaphore.availablePermits());

执行结果:


线程阻塞(二),Semaphore介绍_第1张图片

由于每次占用3个座位,用完后,释放一个座位,另外2个座位本来不用了,但是没有释放,那么别人也用不了,造成资源浪费,同时也影响往下执行。

总结:
Semaphore能锁住资源,比较适合在资源固定,使用者较多的时候,也就是僧多粥少的情况下使用,另外,acquire个数和release个数最好一致。

你可能感兴趣的:(线程阻塞(二),Semaphore介绍)