上一篇文章中详细分析了基于AQS的ReentrantLock原理,ReentrantLock通过AQS中的state变量0和1之间的转换代表了独占锁。那么可以思考一下,当state变量大于1时代表了什么?J.U.C中是否有基于AQS的这种实现呢?如果有,那他们都是怎么实现的呢?这些疑问通过详细分析J.U.C中的Semaphore与CountDownLatch类后,将会得到解答。
Semaphore与CountDownLatch的共享逻辑
Semaphore与CountDownLatch的使用示例
2.1 Semaphore的使用
2.2 CountDownLatch的使用
源码分析
3.1 AQS中共享锁的实现
3.2 Semaphore源码分析
3.3 CountDownLatch源码分析
总结
1. Semaphore与CountDownLatch的共享方式独占锁意味着只能有一个线程获取锁,其他的线程在锁被占用的情况下都必须等待锁释放后才能进行下一步操作。由此类推,共享锁是否意味着可以由多个线程同时使用这个锁,不需要等待呢?如果是这样,那锁的意义也就不存在了。在J.U.C中共享意味着有多个线程可以同时获取锁,但这个多个是有限制的,并不是无限个,J.U.C中通过Semaphore与CountDownLatch来分别实现了两种有限共享锁。
Semaphore又叫信号量,他通过一个共享的’信号包‘来给每个使用他的线程来分配信号,当信号包中的信号足够时,线程可以获取锁,反之,信号包中信号不够了,则不能获取到锁,需要等待足够的信号被释放,才能获取。
CountDownLatch又叫计数器,他通过一个共享的计数总量来控制线程锁的获取,当计数器总量大于0时,线程将被阻塞,不能够获取锁,只有当计数器总量为0时,所有被阻塞的线程同时被释放。
可以看到Semaphore与CountDownLatch都有一个共享总量,这个共享总量就是通过state来实现的。
2. Semaphore与CountDownLatch的使用示例在详细分析Semaphore与CountDownLatch的原理之前,先来看看他们是怎么使用的,这样方便后续我们理解他们的原理。先知道他是什么?然后再问为什么?下面通过两个示例来详细说明Semaphore与CountDownLatch的使用。
2.1 Semaphore的使用 //初始化10个信号量在信号包中,让ABCD4个线程分别去获取 public static void main(String[] args) throws InterruptedException { Semaphore semaphore = new Semaphore(10); SemaphoreTest(semaphore); } private static void SemaphoreTest(final Semaphore semaphore) throws InterruptedException { //线程A初始获取了4个信号量,然后分3次释放了这4个信号量 Thread threadA = new Thread(new Runnable() { @Override public void run() { try { semaphore.acquire(4); System.out.println(Thread.currentThread().getName() + " get 4 semaphore"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " release 1 semaphore"); semaphore.release(1); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " release 1 semaphore"); semaphore.release(1); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " release 2 semaphore"); semaphore.release(2); } catch (InterruptedException e) { e.printStackTrace(); } } }); threadA.setName("threadA"); //线程B初始获取了5个信号量,然后分2次释放了这5个信号量 Thread threadB = new Thread(new Runnable() { @Override public void run() { try { semaphore.acquire(5); System.out.println(Thread.currentThread().getName() + " get 5 semaphore"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " release 2 semaphore"); semaphore.release(2); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " release 3 semaphore"); semaphore.release(3); } catch (InterruptedException e) { e.printStackTrace(); } } }); threadB.setName("threadB"); //线程C初始获取了4个信号量,然后分1次释放了这4个信号量 Thread threadC = new Thread(new Runnable() { @Override public void run() { try { semaphore.acquire(4); System.out.println(Thread.currentThread().getName() + " get 4 semaphore"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " release 4 semaphore"); semaphore.release(4); } catch (InterruptedException e) { e.printStackTrace(); } } }); threadC.setName("threadC"); //线程D初始获取了10个信号量,然后分1次释放了这10个信号量 Thread threadD = new Thread(new Runnable() { @Override public void run() { try { semaphore.acquire(10); System.out.println(Thread.currentThread().getName() + " get 10 semaphore"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " release 10 semaphore"); semaphore.release(10); } catch (InterruptedException e) { e.printStackTrace(); } } }); threadD.setName("threadD"); //线程A和线程B首先分别获取了4个和5个信号量,总信号量变为了1个 threadA.start(); threadB.start(); Thread.sleep(1); //线程C尝试获取4个发现不够则等待 threadC.start(); Thread.sleep(1); //线程D尝试获取10个发现不够则等待 threadD.start(); }