08 JUC 之 Semaphore

Semaphore是一个独立的类,它只实现了序列号接口,无继承抽象类或实现业务接口的情况。

Semaphore使用的简单案例

使用汽车加油案例,简单介绍Semaphore的使用。油机的台数就可看作是Semaphore的令牌数。一台油机只有在空闲出来时才能给汽车加油。油机状态处于闲置-工作-闲置的状态切换。对应Semaphore的令牌空置-被获取-空置的状态切换。

public class JucTest {
    private final static int OIL_MACHINE_NUM = 3;
    private final static Semaphore semaphore = new Semaphore(OIL_MACHINE_NUM);

    static class Run1 implements Runnable {
        private String name;

        private Run1(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(name + " 开始加油");
                Random random = new Random();
                int speedWait = random.nextInt(20) + 10;
                System.out.println(name + "加油耗时" + speedWait);
                Thread.sleep(speedWait * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + " 加油结束");
            semaphore.release();
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 9; i++) {
            Thread car = new Thread(new Run1("车辆" + i));
            car.start();
        }
    }
}

输出结果:

车辆0 开始加油
车辆1 开始加油
车辆2 开始加油
车辆0加油耗时27
车辆1加油耗时29
车辆2加油耗时23
车辆2 加油结束
车辆3 开始加油
车辆3加油耗时18
车辆0 加油结束
车辆4 开始加油
车辆4加油耗时19
车辆1 加油结束
车辆5 开始加油
车辆5加油耗时17
车辆3 加油结束
车辆6 开始加油
车辆6加油耗时16
车辆5 加油结束
车辆4 加油结束
车辆7 开始加油
车辆8 开始加油
车辆8加油耗时15
车辆7加油耗时12
车辆6 加油结束
车辆7 加油结束
车辆8 加油结束

如果当前油机数量只有1个,即令牌只有一个,那么在一个车正在加油时,其他车就在等待。当车加完油,其他车要么排队加油(公平模式),要么抢占加油(非公平)。

Semaphore的源码分析

内部类Sync

继承AbstractQueuedSynchronizer类,也就是之前文章说的AQS类。

Sync类里提供方法有:

通过构造方法设置令牌数,对应AQS里的state数值。和获取令牌数,返回当前的state数值。

Sync(int permits) {
    setState(permits);
}

final int getPermits() {
    return getState();
}

int nonfairTryAcquireShared(int acquires),用于非公平模式下获取共享锁的状态修改,获取成功,state数值减少,之前的ReentrantLock是获取成功累加,返回state减少后的数值。

boolean tryReleaseShared(int releases),释放共享模式下的锁的状态修改,释放成功,state数值增加。

reducePermits(int reductions),state减少指定数值量,表示令牌个数可一次被使用多个。

int drainPermits(),设置state值等于0 ,表示令牌已被全部取光。

内部类NonfairSync

继承Sync类

通过构造方法接收指定的令牌个数。

tryAcquireShared(int acquires)方法直接跳转到了Sync类的nonfairTryAcquireShared方法。

内部类FairSync

继承Sync类

通过构造方法接收指定的令牌个数。

tryAcquireShared(int acquires)处理流程中会访问AQS的hasQueuedPredecessors方法,查看当前线程是否在队列后方排队。排队就return -1,不排队就通过死循环方式对state值进行修改,并返回修改后的state值。

Semaphore构造方法

默认是非公平的数据获取方式,也可以通过参数来选择使用公平模式

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

acquire方法

直接访问AQS类的acquireSharedInterruptibly方法,获取指定个数的令牌。

当前线程如果已中断,AQS会抛出异常。AQS执行子类的tryAcquireShared方法,修改state的数值,当前修改返回结果小于0,说明令牌当前已经被获取完毕,那么当前线程就要被放在等待队列中,并进入到挂起状态。

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

public void acquire(int permits) throws InterruptedException {
    if (permits < 0) throw new IllegalArgumentException();
    sync.acquireSharedInterruptibly(permits);
}

acquireUninterruptibly方法

直接访问AQS类的acquireShared方法,获取指定个数的令牌。

AQS执行子类的tryAcquireShared方法,修改state的数值,当前修改返回结果小于0,说明令牌当前已经被获取完毕,那么当前线程就要被放在等待队列中,并进入到挂起状态。

public void acquireUninterruptibly() {
    sync.acquireShared(1);
}
public void acquireUninterruptibly(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    sync.acquireShared(permits);
}

release方法

直接访问AQS类的releaseShared方法。

AQS会先执行Sync类的tryReleaseShared方法,更新state的数值增加。更新成功后执行doReleaseShared方法,做后续线程的唤醒工作。

public void release() {
    sync.releaseShared(1);
}
public void release(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    sync.releaseShared(permits);
}

其他方法

availablePermits,获取当前剩余令牌个数

drainPermits,清空令牌数量

hasQueuedThreads,判断是否有等待的线程

getQueueLength,获取当前队列里等待线程的个数

reducePermits,减少指定数量的令牌

小结

  1. Semaphore通过AQS的state字段管理一组令牌。令牌被获取,state要减少,直到为0说明令牌都被获取完毕了,其他获取不到的线程要在队列里等待。令牌释放,那么state就要增加。
  2. Semaphore内部使用的是共享模式的锁方式实现的令牌的获取和释放。
  3. Semaphore被使用的场景,对比现实可以看作是窗口办理业务与等待队伍的场景。窗口空闲出来,队伍就补一个人上去让窗口忙碌起来。窗口可看作是令牌,空闲和忙碌对应令牌被释放和获取。
  4. Semaphore也支持公平和非公平的情况,公平就是队伍的人按顺序补空位,非公平就是队伍的人谁跑的快抢到窗口谁补空位。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值