java并发(4)——ReentrantLock,Condition,Semaphore

ReentrantLock

在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从jdk1.6开始,Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。
看一下ReentrantLock的接口:
java并发(4)——ReentrantLock,Condition,Semaphore_第1张图片

简单的例子

java并发(4)——ReentrantLock,Condition,Semaphore_第2张图片
运行结果:
java并发(4)——ReentrantLock,Condition,Semaphore_第3张图片
如果在不加锁的情况下运行结果,最终的数字不确定(接近10000000):
java并发(4)——ReentrantLock,Condition,Semaphore_第4张图片

ReenTrantLock独有的能力:

  1. ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。
  2. ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。
  3. ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。

中断机制

来看一下ReentrantLock相对于synchronized的第一点特点:ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。
java并发(4)——ReentrantLock,Condition,Semaphore_第5张图片
java并发(4)——ReentrantLock,Condition,Semaphore_第6张图片
运行结果:
java并发(4)——ReentrantLock,Condition,Semaphore_第7张图片

公平锁

在大多数情况下锁的申请都是非公平的,如果线程1首先申请的锁,之后线程2申请锁,当这把锁可用的时候,系统只会从这个锁的等待队列中随机挑选一个。这样容易造成某些线程饿死。而公平锁是排队的遵循先到先得的原则:
java并发(4)——ReentrantLock,Condition,Semaphore_第8张图片
公平锁运行结果:
java并发(4)——ReentrantLock,Condition,Semaphore_第9张图片
非公平锁运行结果:
java并发(4)——ReentrantLock,Condition,Semaphore_第10张图片

Condition

如果理解Object.wait()和Object.notify()那么Condition就比较好理解了,wait和notify是和synchronized关键字配合使用
Condition接口的方法如下:
java并发(4)——ReentrantLock,Condition,Semaphore_第11张图片

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上所有的线程。

示例:
java并发(4)——ReentrantLock,Condition,Semaphore_第12张图片
运行结果:
java并发(4)——ReentrantLock,Condition,Semaphore_第13张图片
和wait/notify一样,当线程使用condition.await()的时候,要求线程持有相关的锁,当线程调用condition.await()之后,这个线程会释放持有的锁,并进入等待状态。当调用notify()的时候系统会挑选一个等待在该条件下的线程并唤醒之。

在JDK内部,重入锁和Condition被广泛使用,以ArrayBlockingQueue为例:
java并发(4)——ReentrantLock,Condition,Semaphore_第14张图片
java并发(4)——ReentrantLock,Condition,Semaphore_第15张图片
java并发(4)——ReentrantLock,Condition,Semaphore_第16张图片

信号量:Semaphore

信号量为多个线程协作提供了更强大的控制方法,无论是ReentrantLock还是Synchronized一次都只允许一个线程访问一个资源。信号量允许多个线程同时访问同一个资源:
看下Semaphore的接口定义:
java并发(4)——ReentrantLock,Condition,Semaphore_第17张图片
有两个构造函数:
java并发(4)——ReentrantLock,Condition,Semaphore_第18张图片
permits表示一次允许多少个线程, fair表示是否公平锁

示例:
java并发(4)——ReentrantLock,Condition,Semaphore_第19张图片

你可能感兴趣的:(Java并发)