1965迪杰斯特拉提出,之后15年是并发编程领域的终结者,直到 1980年管程提出来,才有了第二选择
信号量模型
一个计数器,一个等待队列,三个方法(init、down、up)
计数器和等待队列对外是透明的,只能通过信号量模型提供的三个方法来访问它们
- init
设置计数器的初始值。
- down
计数器值减1,如果计数器的值小于0,则当前线程将被阻塞,否则继续执行。
- up
计数器值加1,如果计数器值小于或者等于0,则唤醒等待队列中的一个线程,并将其移除。
快速实现一个限流器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class ObjPool<T, R> { final List<T> pool; // 用信号量实现限流器 final Semaphore sem; // 构造函数 ObjPool(int size, T t){ pool = new Vector<T>(){}; for(int i=0; i<size; i++){ pool.add(t); } sem = new Semaphore(size); } // 利用对象池的对象,调用 func R exec(Function<T,R> func) { T t = null; sem.acquire(); try { t = pool.remove(0); return func.apply(t); } finally { pool.add(t); sem.release(); } } } // 创建对象池 ObjPool<Long, String> pool = new ObjPool<Long, String>(10, 2); // 通过对象池获取 t,之后执行 pool.exec(t -> { System.out.println(t); return t.toString(); });
|
注意:Semaphore 可以允许多个线程访问一个临界区,所以在acquire后的操作要保证线程安全
思考题
- 对象保存在了 Vector 中,Vector 是 Java 提供的线程安全的容器,如果我们把 Vector 换成 ArrayList,是否可以呢?
不可,semophore允许多线程访问临界区,即多线程同时调用pool.remove方法时应保证线程安全