java中的计数信号量(Counting Semaphore)

信号量(Semaphore)又称为信号量、旗语,它以一个整数变数,提供信号,以确保在并行计算环境中,不同进程在访问共享资源时,不会发生冲突。是一种不需要使用忙碌等待(busy waiting)的一种方法。

信号量的概念是由荷兰计算机科学家艾兹格·迪杰斯特拉(Edsger W. Dijkstra)发明的,广泛的应用于不同的操作系统中。在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来。如果信号量是一个任意的整数,通常被称为计数信号量(Counting semaphore),或一般信号量(general semaphore);如果信号量只有二进制的0或1,称为二进制信号量(binary semaphore)。在linux系中,二进制信号量(binary semaphore)又称Mutex。

java中实现的是计数信号量,用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界。

java中实现计数信号量的类为java.util.concurrent.Semaphore,是在1.5中引入的。Semaphore中管理着一组许可,许可的初始数量可以通过构造方法来指定。在执行操作时需要先获得许可(acquire),并在使用完后释放许可(release)。如果当前没有许可,那么acquire将阻塞下到有许可可用,或者直到被中断,或者操作超时。

使用方式

使用方式可以像下面这样:

package com.mikan.thread;

import java.util.concurrent.Semaphore;

/**
 * @author Mikan
 * @date 2015-08-29 18:40
 */
public class SemaphoreTest {

    private Semaphore semaphore;

    public SemaphoreTest(int permits) {
        if (permits <= 0) {
            throw new IllegalArgumentException("permits must be greater than 0");
        }
        semaphore = new Semaphore(permits);
    }

    public void operation() throws InterruptedException {
        semaphore.acquire();
        try {
            // do something......
        }
        finally {
            semaphore.release();
        }
    }
}
Semaphore的实现是基于java.util.concurrent.locks.AbstractQueuedSynchronizer(AQS)基类,JDK中许多同步类都是基于它来实现的,像ReentrantLock、CountDownLatch、ReentrantReadWriteLock等。Semaphore的实现使用AQS的状态来保存许可数量,它实现了公平和非公平两种策略,默认下是非公平策略,可以在创建Semaphore时指定公平策略。当许可为1时,可以当作互斥锁来使用。

使用Semaphore来实现有界容器

比如这里使用Semaphore来实现一个有指定容量的List:

package com.mikan.thread;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;

/**
 * @author Mikan
 * @date 2015-08-29 19:11
 */
public class BoundedList<T> {

    private final List<T> list;
    private final Semaphore semaphore;

    public BoundedList(int bound) {
        list = Collections.synchronizedList(new LinkedList<T>());
        semaphore = new Semaphore(bound);
    }

    public boolean add(T obj) throws InterruptedException {
        semaphore.acquire();
        boolean addedFlag = false;
        try {
            addedFlag = list.add(obj);
        }
        finally {
            if (!addedFlag) {
                semaphore.release();
            }
        }
        return addedFlag;
    }

    public boolean remove(Object obj) {
        boolean removedFlag = list.remove(obj);
        if (removedFlag) {
            semaphore.release();
        }
        return removedFlag;
    }
    
    // 其他操作委托给底层的List,这里只列举出一个方法
    public T get(int index) {
        return list.get(index);
    }
    
    // 其他方法……

}

使用信号量来控制同时执行的线程数量

package com.mikan.thread;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;

/**
 * @author Mikan
 * @date 2015-08-29 19:47
 */
public class BoundedThreadExecution {

    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(5);
        final CountDownLatch gate = new CountDownLatch(1);
        int maxThreads = 100;
        for (int i = 0; i < maxThreads; i++) {
            final int index = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        gate.await();
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + "-->" + index + "--available permits=" + semaphore.availablePermits());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Thread.currentThread().interrupt();
                    }
                    finally {
                        // 不管当前线程是否正常结束,都释放许可
                        semaphore.release();
                    }
                }
            }).start();

        }
        // 使用闭锁来实现所有线程同时开始执行
        gate.countDown();
    }

}

自己实现一个计数信号量

package com.mikan.thread;

/**
 * @author Mikan
 * @date 2015-08-29 19:19
 */
public class CountingSemaphore {

    private final int bound;
    private int permits = 0;

    public CountingSemaphore(int permits) {
        if (permits <= 0) {
            throw new IllegalArgumentException("permits must be greater than 0");
        }
        this.bound = permits;
    }

    public synchronized void acquired() throws InterruptedException {
        // 当许可达到上限时,则阻塞
        while (permits == bound) {
            wait();
        }
        permits++;
    }

    public synchronized void release() {
        permits--;
        // 释放了许可,通知等待的线程
        notifyAll();
    }
}

展开阅读全文

没有更多推荐了,返回首页