什么是Semaphore:
信号量
在前面的学习中,我们使用了synchronized加锁,后面reentrantLock加锁从而实现多线程并发运行,但这两个的特点都是:在一段时间内只允许一个线程处于允许状态,其他线程都处于阻塞状态
那么咱们的Semaphore就可以在某个时间指定数量个线程运行
主要方法:
1.构造方法:指定初始资源的数量 (int permits) 是否为公平锁(boolean fair)
public Semaphore(int permits) { sync = new NonfairSync(permits); } public Semaphore(int permits, boolean fair){ sync = fair ? new FairSync(permits) : new NonfairSync(permits); }
2.acquire():获取资源 如果是无参 ,默认获取一个资源
public void acquire(int permits) throws InterruptedException { sync.acquireSharedInterruptibly(1); }
3.release():释放资源 无参,默认释放一个资源
public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits); }
acquire:
public static void main(String[] args) throws InterruptedException { Semaphore semaphore = new Semaphore(4); System.out.println("取了一个资源"); semaphore.acquire(); //默认取一个资源 System.out.println("取了三个资源 资源被取完"); semaphore.acquire(3); //此时资源量已经为0了 如果继续取资源就会阻塞 semaphore.acquire(); System.out.println("被阻塞 执行不到下面代码"); }
创建Semaphore对象时初始化资源类值,当acquire完所有资源后,再acquire此时就会阻塞
release():
public static void main(String[] args) throws InterruptedException { Semaphore semaphore = new Semaphore(4); System.out.println("取了一个资源"); semaphore.acquire(); //默认取一个资源 System.out.println("取了三个资源 资源被取完"); semaphore.acquire(3); //释放一个资源 此后又可以取了 System.out.println("释放一个资源"); semaphore.release(); //取资源 semaphore.acquire(); System.out.println("取一个资源"); }
此方法为释放资源,使资源量+1,实例化Semaphore对象时给出的资源量初始值并不是最大资源数,release可以往上继续增加资源量
public static void main(String[] args) throws InterruptedException { Semaphore semaphore = new Semaphore(4); semaphore.release(1); semaphore.acquire(4); semaphore.acquire(); System.out.println("获取了5个资源量"); }
模拟停车场系统:
public static void main(String[] args) { //通过Semaphore实现停车场停车位实例 //规定停车场有5个停车位 Semaphore semaphore = new Semaphore(5); //lambda表达式对原子类不会变量捕获 AtomicInteger comeCar = new AtomicInteger(); Thread come = new Thread(() -> { while (true) { try { //随机5-10秒内 就会进来一辆车 Thread.sleep(new Random().nextInt(1000) + 1000); semaphore.acquire(); comeCar.getAndIncrement(); System.out.println("come:" + comeCar + "辆车进来了"); } catch (InterruptedException e) { e.printStackTrace(); } } }); AtomicInteger outCar = new AtomicInteger(); Thread out = new Thread(() -> { while (true) { //随机10-20秒内 就会出去一辆车 try { Thread.sleep(new Random().nextInt(1000) + 2000); //释放资源 semaphore.release(); outCar.getAndIncrement(); System.out.println("out:有" + outCar + "辆车出去了"); } catch (InterruptedException e) { e.printStackTrace(); } } }); come.start(); out.start(); }
当车停满后,车辆就进不来了,除非有车出停车场,Semaphore很匹配这个场景
两个线程交替打印
public static void main(String[] args) { //使用两个信号量控制两个线程交替打印 //如果只使用一个信号量是不好控制的 由于线程调度的随机性 或多或少都会出现几次极端情况 Semaphore s1 = new Semaphore(1); Semaphore s2 = new Semaphore(0); Thread t1 = new Thread(() -> { for(int i = 0 ;i < 5; i++){ try { s1.acquire(); System.out.println("my name is KiKi"); s2.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(() -> { for(int i = 0 ;i < 5; i++){ try { s2.acquire(); System.out.println("my name is LaLa"); s1.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); }
使用两个信号量,一个控制t1线程,一个控制t2线程,当t1线程完成打印后,释放线程2的信号量,使t2线程可以打印,t2线程完成打印后,再释放线程1的信号量
三个线程交替打印
public static void main(String[] args) throws InterruptedException { Semaphore s1 = new Semaphore(1); Semaphore s2 = new Semaphore(0); Semaphore s3 = new Semaphore(0); Thread t1= new Thread(() -> { for(int i = 0; i < 3; i++){ try { s1.acquire(); System.out.print("A "); s2.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(() -> { for(int i = 0; i < 3; i++){ try { s2.acquire(); System.out.print("B "); s3.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t3 = new Thread(() -> { for(int i = 0; i < 3; i++){ try { s3.acquire(); System.out.print("C "); s1.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); t3.start(); }