信号量的实现模型一般包括:1个计数器、1个等待队列、3个方法(需要保证原子性)
Semaphore 实现的伪代码(JDK 中 Semaphore 是基于 AbstractQueuedSynchronizer 实现,可以指定是否公平):
class Semaphore{ //计数器 int count; //等待队列 Queue queue; //初始化 Semaphore(int c){ this.count=c; } //获取许可证 void acquire(){ count--; if(count<0){ //将当前线程插入等待队列 //阻塞当前线程 } } //获取许可证 void release(){ count++; if(count<=0) { //移除等待队列中的某个线程 //唤醒某个线程 } } }
使用信号量实现互斥锁效果:
package constxiong.interview; import java.util.concurrent.Semaphore; /** * 测试使用信号量实现锁的效果 * @author ConstXiong * @date 2019-12-18 14:18:47 */ public class TestSemaphore { private static int count; private static Semaphore semaphore = new Semaphore(1); public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(() -> { add(); System.out.println(count); }).start(); } } private static void add() { try { semaphore.acquire(); Thread.sleep(100); count++; } catch(InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } } }
除了能实现互斥锁,信号量还可以做到允许多个线程访问同一个临界区,这是它与互斥锁一个较大的区别点。
将代码进行修改,实现限流功能:
package constxiong.interview; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; /** * 测试使用信号量实现限流的效果 * @author ConstXiong * @date 2019-12-18 14:18:47 */ public class TestSemaphore { private static AtomicInteger acount = new AtomicInteger(0); private static Semaphore semaphore = new Semaphore(10); public static void main(String[] args) { testAddAtomic(); } /** * 测试允许十个线程并发递增 acount */ private static void testAddAtomic() { for (int i = 0; i < 100; i++) { new Thread(() -> { System.out.println(addAtomic()); }).start(); } } private static int addAtomic() { try { semaphore.acquire(); Thread.sleep(100); return acount.incrementAndGet(); } catch(InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } return -1; } }
在实际的 Java 开发中,信号量的使用相对互斥锁来说较少,知名度没那么高,但在其他编程语言中使用较广。
- Java 自学指南
- Java 面试题汇总PC端浏览【点这里】
- Java知识图谱
- Java 面试题汇总小程序浏览,扫二维码