深入并发编程-Semaphore

Semaphore
n. 信号标,旗语;臂板信号装置
v. 打旗语,发信号

只要看过AQS这个还是很好懂的

1. 什么是信号量?

如果学过操作系统,我们就会知道有一个PV操作,PV操作是操作系统中的一种同步机制,实现对于并发进程中临界区的管理。 而Semaphore就是支持这样的操作flag 即信号量; 往往代表了资源数;

P(S):S <—— S - 1

      如果S >= 0,进程继续执行

      如果S < 0,进程停止执行,放入信号量等待队列中。

V(S):S <—— S +1

       如果S > 0,进程继续执行;

       如果S <= 0,唤醒等待队列中的一个进程。

2. JAVA-Semaphore

public class Semaphore implements java.io.Serializable {
    private static final long serialVersionUID = -3222578661600680210L;
    /** 所有机制通过AbstractQueuedSynchronizer子类 */
    private final Sync sync;

    /**
     * 信号量的同步实现。使用aq状态
	 *代表许可证。分为公平和不公平
	 *版本。
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

可见它是由AQS实现的

AQS可参考

JDK的源码注释翻译如下:

一个计数信号量。 在概念上,信号量维持一组许可证。 如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。 每个release()添加许可证,潜在地释放阻塞获取方。 但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行。

信号量通常用于限制线程数,而不是访问某些(物理或逻辑)资源。 例如,这是一个使用信号量来控制对一个项目池的访问的类:

class Pool {
    private static final int MAX_AVAILABLE = 100;
    private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
    // Not a particularly efficient data structure; just for demo
    //... whatever kinds of items being managed
    protected Object[] items = new Object[10];
    protected boolean[] used = new boolean[MAX_AVAILABLE];

    public Object getItem() throws InterruptedException {
        available.acquire();
        return getNextAvailableItem();
    }

    public void putItem(Object x) {
        if (markAsUnused(x))
            available.release();
    }

    protected synchronized Object getNextAvailableItem() {
        for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (!used[i]) {
                used[i] = true;
                return items[i];
            }
        }
        return null; // not reached
    }

    protected synchronized boolean markAsUnused(Object item) {
        for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (item == items[i]) {
                if (used[i]) {
                    used[i] = false;
                    return true;
                } else
                    return false;
            }
        }
        return false;
    }
}

在获得项目之前,每个线程必须从信号量获取许可证,以确保某个项目可用。 当线程完成该项目后,它将返回到池中,并将许可证返回到信号量,允许另一个线程获取该项目。
请注意,当调用acquire()时,不会保持同步锁定,因为这将阻止某个项目返回到池中。 信号量封装了限制对池的访问所需的同步,与保持池本身一致性所需的任何同步分开。

信号量被初始化为一个,并且被使用,使得它只有至多一个允许可用,可以用作互斥锁。
这通常被称为二进制信号量 ,因为它只有两个状态:
一个许可证可用,或零个许可证可用。 当以这种方式使用时,二进制信号量具有属性(与许多Lock实现不同),“锁”可以由除所有者之外的线程释放(因为信号量没有所有权概念)。 这在某些专门的上下文中是有用的,例如死锁恢复。

此类的构造函数可选择接受公平参数。 当设置为false时,此类不会保证线程获取许可的顺序。 特别是, 闯入是允许的,也就是说,一个线程调用acquire()可以提前已经等待线程分配的许可证-在等待线程队列的头部逻辑新的线程将自己。 当公平设置为真时,信号量保证调用acquire方法的线程被选择以按照它们调用这些方法的顺序获得许可(先进先出; FIFO)。

请注意,FIFO排序必须适用于这些方法中的特定内部执行点。 因此,一个线程可以在另一个线程之前调用acquire ,但是在另一个线程之后到达排序点,并且类似地从方法返回。 另请注意, 未定义的tryAcquire方法不符合公平性设置,但将采取任何可用的许可证。

通常,用于控制资源访问的信号量应该被公平地初始化,以确保线程没有被访问资源。 当使用信号量进行其他类型的同步控制时,非正常排序的吞吐量优势往往超过公平性。

本课程还提供了方便的方法, 一次acquire和release多个许可证。 当没有公平地使用这些方法时,请注意增加无限期延期的风险。

内存一致性效应:在另一个线程中成功执行“获取”方法(如acquire()之前,调用“释放”方法之前的线程中的操作,例如release() happen-before

默认是一个非公平锁

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

3. 使用Semaphore

除了上面JDK注释里面提供的一种使用方式,这里再贴一个简单的使用方式

 public static void main(String[] args) {
 // 并发竞争3个资源 
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 6; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();

                        System.out.println(Thread.currentThread().getName() + "得到资源-acquire");
                        TimeUnit.SECONDS.sleep(2);
                        System.out.println(Thread.currentThread().getName() + "释放资源-release");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();
                    }

                }
            }, String.valueOf(i) + "线程").start();

        }
    }

4. PV操作实现

多个信号量组合实现各种模型

比如缓冲区多路进 多路出
独木桥
哲学家问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值