java并发(4)——ReentrantLock,Condition,Semaphore 梦里梦外; 2022-05-28 08:12 144阅读 0赞 # ReentrantLock # 在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从jdk1.6开始,Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。 看一下ReentrantLock的接口: ![这里写图片描述][70] ## 简单的例子 ## ![这里写图片描述][70 1] 运行结果: ![这里写图片描述][70 2] 如果在不加锁的情况下运行结果,最终的数字不确定(接近10000000): ![这里写图片描述][70 3] ## ReenTrantLock独有的能力: ## 1. ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。 2. ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。 3. ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。 ## 中断机制 ## 来看一下ReentrantLock相对于synchronized的第一点特点:ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。 ![这里写图片描述][70 4] ![这里写图片描述][70 5] 运行结果: ![这里写图片描述][70 6] ## 公平锁 ## 在大多数情况下锁的申请都是非公平的,如果线程1首先申请的锁,之后线程2申请锁,当这把锁可用的时候,系统只会从这个锁的等待队列中随机挑选一个。这样容易造成某些线程饿死。而公平锁是排队的遵循先到先得的原则: ![这里写图片描述][70 7] 公平锁运行结果: ![这里写图片描述][70 8] 非公平锁运行结果: ![这里写图片描述][70 9] ## Condition ## 如果理解Object.wait()和Object.notify()那么Condition就比较好理解了,wait和notify是和synchronized关键字配合使用 Condition接口的方法如下: ![这里写图片描述][70 10] void await() throws InterruptedException 当前线程进入等待状态,直到被通知(signal)或者被中断时,当前线程进入运行状态,从await()返回; void awaitUninterruptibly() 当前线程进入等待状态,直到被通知,对中断不做响应; long awaitNanos(long nanosTimeout) throws InterruptedException 在`await()`的返回条件基础上增加了超时响应,返回值表示当前剩余的时间,如果在nanosTimeout之前被唤醒,返回值 = nanosTimeout - 实际消耗的时间,返回值 <= 0表示超时; boolean await(long time, TimeUnit unit) throws InterruptedException 同样是在`await()`的返回条件基础上增加了超时响应,与接口3不同的是: 可以自定义超时时间单位; 返回值返回true/false,在time之前被唤醒,返回true,超时返回false。 boolean awaitUntil(Date deadline) throws InterruptedException 当前线程进入等待状态直到将来的指定时间被通知,如果没有到指定时间被通知返回true,否则,到达指定时间,返回false; void signal() 唤醒一个等待在Condition上的线程; void signalAll() 唤醒等待在Condition上所有的线程。 示例: ![这里写图片描述][70 11] 运行结果: ![这里写图片描述][70 12] 和wait/notify一样,当线程使用condition.await()的时候,要求线程持有相关的锁,当线程调用condition.await()之后,这个线程会释放持有的锁,并进入等待状态。当调用notify()的时候系统会挑选一个等待在该条件下的线程并唤醒之。 在JDK内部,重入锁和Condition被广泛使用,以ArrayBlockingQueue为例: ![这里写图片描述][70 13] ![这里写图片描述][70 14] ![这里写图片描述][70 15] ## 信号量:Semaphore ## 信号量为多个线程协作提供了更强大的控制方法,无论是ReentrantLock还是Synchronized一次都只允许一个线程访问一个资源。信号量允许多个线程同时访问同一个资源: 看下Semaphore的接口定义: ![这里写图片描述][70 16] 有两个构造函数: ![这里写图片描述][70 17] permits表示一次允许多少个线程, fair表示是否公平锁 示例: ![这里写图片描述][70 18] [70]: /images/20220528/7c303c9e7dcd419c987734d3705c3281.png [70 1]: /images/20220528/acc2d8f04ef9433cb9d30dc7522a70e3.png [70 2]: /images/20220528/73356d27925d4d83ae4a188063af3fd9.png [70 3]: /images/20220528/0d1f66f5837e43bbb3effeaee2ea2ee5.png [70 4]: /images/20220528/9d3ccd7c4020488097fe880b89aec389.png [70 5]: /images/20220528/0e6cd481cd4440d9ad59dd9d0a5773fe.png [70 6]: /images/20220528/2a48582eae2e49d1a21396a5053d8023.png [70 7]: /images/20220528/dca761ae15884671a51c44e4f31b55ba.png [70 8]: /images/20220528/3cb511e57b8f4294958ac95730661cb4.png [70 9]: /images/20220528/b19787280cb04ccea8520d980b4e5835.png [70 10]: /images/20220528/1a74e8e9df284cd3a6e6a8bc1691315e.png [70 11]: /images/20220528/b1f444b4a82e40568381819926ab2466.png [70 12]: /images/20220528/c5aaf69ffbc245448245ccb6dcd8dde5.png [70 13]: /images/20220528/44dfee1f4ce84d77a9a7bb3e2e53ac82.png [70 14]: /images/20220528/b3b2f6bddb7e46cc85b89f7fc572a350.png [70 15]: /images/20220528/3c842e31d2174c0da44db9d02190e8a5.png [70 16]: /images/20220528/bbb5292f22f24828ac63a6dab968aee5.png [70 17]: /images/20220528/d6e58b7aa9d04818949156f6edd9e992.png [70 18]: /images/20220528/666ea2c1cb774fd2b4134619ff1c8872.png
还没有评论,来说两句吧...