在Synchronized优化以前,synchronized的性能是比ReenTrantLock差很多的,但是自从jdk1.6开始,Synchronized引入了偏向锁,轻量级锁(自旋锁)后,两者的性能就差不多了,在两种方法都可用的情况下,官方甚至建议使用synchronized,其实synchronized的优化我感觉就借鉴了ReenTrantLock中的CAS技术。都是试图在用户态就把加锁问题解决,避免进入内核态的线程阻塞。
看一下ReentrantLock的接口:
运行结果:
如果在不加锁的情况下运行结果,最终的数字不确定(接近10000000):
来看一下ReentrantLock相对于synchronized的第一点特点:ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。
运行结果:
在大多数情况下锁的申请都是非公平的,如果线程1首先申请的锁,之后线程2申请锁,当这把锁可用的时候,系统只会从这个锁的等待队列中随机挑选一个。这样容易造成某些线程饿死。而公平锁是排队的遵循先到先得的原则:
公平锁运行结果:
非公平锁运行结果:
如果理解Object.wait()和Object.notify()那么Condition就比较好理解了,wait和notify是和synchronized关键字配合使用
Condition接口的方法如下:
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上所有的线程。
示例:
运行结果:
和wait/notify一样,当线程使用condition.await()的时候,要求线程持有相关的锁,当线程调用condition.await()之后,这个线程会释放持有的锁,并进入等待状态。当调用notify()的时候系统会挑选一个等待在该条件下的线程并唤醒之。
在JDK内部,重入锁和Condition被广泛使用,以ArrayBlockingQueue为例:
信号量为多个线程协作提供了更强大的控制方法,无论是ReentrantLock还是Synchronized一次都只允许一个线程访问一个资源。信号量允许多个线程同时访问同一个资源:
看下Semaphore的接口定义:
有两个构造函数:
permits表示一次允许多少个线程, fair表示是否公平锁