信号量Semaphore的简单使用

一. 什么是信号量

        使用synchronized或lock锁是确保某一区域 "只能有一个线程执行", 而信号量Semaphore是对这一种模式的拓展, 以确保某个区域 "最多只能由N个线程执行", 假设能有使用的资源个数有N个, 而需要这些资源的线程由多余N个, 这就会导致资源竞争, 因此需要 "交通管制", 这种情况下也需要用到计数信号量

二. 常见的方法

Semaphore是java.util.concurrent包下面提供的类

1. 资源允许个数(permits)将通过Semaphore的构造函数来指定:

        java.util.concurrent.Semaphore#Semaphore(int)

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

        最终会设置一个同步阻塞队列,  队列的长度就是资源允许的个数

        java.util.concurrent.locks.AbstractQueuedSynchronizer#setState

protected final void setState(int newState) {
        state = newState;
    }

2. Semaphore的acquire()方法用于确保存在可用资源, 当存在可用资源时, 线程会立即从acquire()方法放回true, 同时信号量内部的资源个数会减去1, 如果没有可用资源, 线程会阻塞在acquire()方法内, 直到出现可用资源

        java.util.concurrent.Semaphore#acquire()

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



public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

3. Semaphore的release()方法会释放资源, 释放资源后, 信号量内部的资源个数会增加1, 另外, 如果acquire()方法中存在等待线程, 那么其中一个线程会被唤醒, 并从acquire()方法返回

        java.util.concurrent.Semaphore#release()

public void release() {
        sync.releaseShared(1);
    }


private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;

        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        // 如果存在等待线程, 会唤醒它
        if (s != null)
            LockSupport.unpark(s.thread);
    }

三. 示例程序

        首先是资源池

/**
 * @author canxiusi.yan
 * @description BoundedResource
 * @date 2022/5/27 11:57
 */
public class BoundedResource {

    private final Semaphore semaphore;
    private final int permits;
    private final static Random R = new Random();

    public BoundedResource(int permits) {
        this.semaphore = new Semaphore(permits);
        this.permits = permits;
    }

    public void use() throws InterruptedException {
        semaphore.acquire();
        try {
            doUse();
        } finally {
            semaphore.release();
        }
    }

    private void doUse() throws InterruptedException {
        Log.println("begin : used = " + (permits - semaphore.availablePermits()));
        Thread.sleep(R.nextInt(500));
        Log.println("end : used = " + (permits - semaphore.availablePermits()));
    }

    public static class Log {

        public static void println(String s) {
            System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName() + ":  " + s);
        }
    }

}

        其次是使用资源的线程

/**
 * @author canxiusi.yan
 * @description UserThread
 * @date 2022/5/27 12:05
 */
public class UserThread extends Thread {

    private final static Random R = new Random();
    private final BoundedResource boundedResource;

    public UserThread(BoundedResource boundedResource) {
        this.boundedResource = boundedResource;
    }

    @Override
    public void run() {
        try {
            while (true) {
                boundedResource.use();
                Thread.sleep(R.nextInt(3000));
            }
        } catch (InterruptedException e) {
            //
        }
    }

    public static void main(String[] args) {
        // 设置3个资源
        BoundedResource boundedResource = new BoundedResource(3);

        // 10个线程使用
        for (int i = 0; i < 10; i++) {
            new UserThread(boundedResource).start();
        }
    }
}

        运行结果, 可以看到, 虽然10个线层交替使用资源, 但是同时使用的资源最多只能是3个

如有错误, 请指正, 谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值