Semaphore

semaphore翻译为信号量,它用来做什么用呢?——看JDK文档:

Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource. For example, here is a class that uses a semaphore to control access to a pool of items:

由这段英文可知,semaphore是限制线程数量的,原因往往是资源有限。接着JDK文档举了一个例子,由semaphore来限制对象池的访问。对象池的大小是固定的,其中资源的访问需要限制。

class Pool {
   private static final int MAX_AVAILABLE = 100;
   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

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

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

   // Not a particularly efficient data structure; just for demo

   protected Object[] items = ... whatever kinds of items being managed
   protected boolean[] used = new boolean[MAX_AVAILABLE];

   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;
   } 

对象池中的对象数量是100,所以semaphore的初始值也是100。获取对象之前,必须通过semaphore的通过。getNextAvailableItem()被synchronized修饰,这是防止并发情况下,返回同一个对象,并且标记对象的使用状态。归还对象池中对象时,需要增加semaphore的值availablePermits()。

这段代码也可以看到Pool的设计思想。

想到2016年遇到的一道面试题,假设有N个线程,依次打印0, 1, 2, N-1, N, N+1, N+2 ...

2N-1……

审题可知,这道题主要考察多线程之间的同步。多个线程之间循环同步,而且输出保持有序性,可知每次输出只能有一个线程打印,这里刚好遇到semaphore.

class Worker4 implements Runnable {
    private int x;
    private int co;
    Semaphore semaphore;

    public Worker4(int x, int co, Semaphore semaphore) {
        this.x = x;
        this.co = co;
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        while (x < 1000) {
            try {
                semaphore.acquire();
                if (x == ConditionTest.n + 1) {
                    System.out.println(Thread.currentThread().getName() + "\t" + (++ConditionTest.n));
                    x += co;
                }
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(1, false);
        System.out.println(semaphore.availablePermits());
        // n 是线程数量
        int n = ThreadLocalRandom.current().nextInt(10, 20);
        for (int i = 0; i < n; i++) {
            Thread thread = new Thread(new Worker4(i, n, semaphore));
            thread.start();
        }
}

2019-07-18
为什么要用两个“锁”?这个问题,我很早就觉察了。现在重看代码才明白。
内部锁是必须的,因为因为get 和 put 都不是原子操作。
信号量是为了控制线程数量的,这样只会让少量的线程去竞争内部锁。
如果没有信号量,那所有的线程都会抢占内部锁,这无疑是不合理的。
所以,信号量有限流的作用。

参考资料:Semaphore (Java Platform SE 8 )

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容