一.信号量

Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

二.信号量模型

概况为:一个计数器,一个等待队列,三个方法。在信号模型里,计数器和等待队列对外是透明

,所以只能通过信号模型提供的三个方法来访问它们,这三个方法分别是:init()、down()、up()。

Java并发Semaphore_java

这三个方法详细语义:

  • init():设置计数器初始值。
  • down():计数器的值减1;如果此时计数器的值小于0,则当前线程将阻塞,否则当前线程可以继续执行。
  • up():计数器的值加1;如果此时计数器的值小于或者等于0,则唤醒等待队列中一个线程,并将其从等待队列中移除。

这里提到的 init()、down() 和 up() 三个方法都是原子性的,并且这个原子性是由信号量模型的实现方保证的。在 Java SDK 里面,信号量模型是由 java.util.concurrent.Semaphore 实现的,Semaphore 这个类能够保证这三个方法都是原子操作。

// Java SDK 并发包里,down() 和 up() 对应的则是 acquire() 和 release()。
class Semaphore{
  // 计数器
  int count;
  // 等待队列
  Queue queue;
  // 初始化操作
  Semaphore(int c){
    this.count=c;
  }
  // 
  void down(){
    this.count--;
    if(this.count<0){
      //将当前线程插入等待队列
      //阻塞当前线程
    }
  }
  void up(){
    this.count++;
    if(this.count<=0) {
      //移除等待队列中的某个线程T
      //唤醒线程T
    }
  }
}

三.使用信号量

3.1 示例-累加器

count+=1操作是个临界区,只允许一个线程执行,也就是说要保证互斥。

public class SemaphoreTest {
    static int count;
    static final Semaphore s = new Semaphore(1);
    static void addOne(){
        try {
            s.acquire();
            count +=1;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            s.release();
        }
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            SemaphoreTest.addOne();
        });
        Thread t2 = new Thread(()->{
            SemaphoreTest.addOne();
        });
        Thread t3 = new Thread(()->{
            SemaphoreTest.addOne();
        });

        t1.start();
        t2.start();
        t3.start();
        try {
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(SemaphoreTest.count);
    }
}

3.2 示例-限流器

Semaphore 有个功能是Lock不容易实现,就是Semaphore可以允许多个线程访问一个临界区。

例如:对象池,线程池,都会用到类似功能。

/**
 * 对象池,指的一次性创建出N个对象,之后所有线程重复利用这N个
 * 对象,当然对象在被释放前,也是不容许其他线程使用。 *
 * 对象的互斥性
 */
public class ObjPool<T,R> {
    //用信号量实现限流器
    final List<T> pool;
    //构造函数
    final Semaphore sem;
    public ObjPool(T[] tArray){
        pool = new Vector<T>(){};
        int size = tArray.length;
        for (int i = 0; i < tArray.length; i++) {
            pool.add(tArray[i]);
        }
        sem = new Semaphore(size);
    }
    R exec(Function<T,R> func) throws InterruptedException {
        T t = null;
        try {
            sem.acquire();
            t = pool.remove(0);
            return func.apply(t);
        }finally {
            pool.add(t);
            sem.release();
        }
    }
    public static void main(String[] args) {
        String[] mess = new String[10];
        for (int i = 0; i < 10; i++) {
            mess[i] = "obj_"+i;
        }
        ObjPool<String, String> objPool = new ObjPool<>(mess);
        for (int i = 0; i < 100; i++) {
            Thread t1= new Thread(()->{
                try {
                    objPool.exec(t -> {
                        System.out.println("当前线程id:"+Thread.currentThread().getId()+",当前获取到的对象:"+t);
                        return t;
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            t1.start();
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

参考

《Java并发编程实战》

公众号

Java并发Semaphore_多线程_02

微信公众号(bigdata_limeng)