这两天抽了点时间看了关于ReentrantLock的博客,上一篇的就是其中一篇。自己看完也来复习一下先。
ReentrantLock是一个可重入的互斥(独占)锁,又称为“独占锁”。
ReentrantLock通过自定义队列同步器(AQS-AbstractQueuedSynchronized,是显示锁的关键)来实现锁的获取与释放。其可以完全替代synchronized关键字。JDK5.0早期,其性能远好于synchronized关键字,但从JDK6.0开始,JDK对synchronized做了大量的优化,使得两者差距并不大。
“
独占”:就是在同一时刻只能有一个线程获取到锁,而其它获取锁的线程只能处于同步队列中的等待,只有获取锁的线程释放了锁,后续的线程才能获取锁。
“
可重入”:就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁,
是指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞。
该锁还支持获取锁时的公平和非公平选择。“公平”是指不同的线程获取锁的机制是公平的,而“不公平”是指不同的线程获取锁的机制是非公平的。默认的锁是非公平的。
还有最后我们需要注意一下的是:最后一定要释放锁,不然会造成锁永远无法释放,其他线程永远进不来的结果。
1、下面我们先看一下简单的使用例子:
public class UseReentrantLock {
private Lock lock = new ReentrantLock();
public void methos1(){
try{ //一般都是配合try-catch-finally使用
lock.lock();
System.out.println("线程:"+Thread.currentThread().getName()+"获取了锁进入方法1");
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}finally{ //在最后释放锁
lock.unlock();
System.out.println("线程:"+Thread.currentThread().getName()+"释放锁退出方法1");
}
}
public void methos2(){
try{ //一般都是配合try-catch-finally使用
lock.lock();
System.out.println("线程:"+Thread.currentThread().getName()+"获取了锁进入方法2");
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}finally{ //在最后释放锁
lock.unlock();
System.out.println("线程:"+Thread.currentThread().getName()+"释放锁退出方法2");
}
}
public static void main(String[] args) {
UseReentrantLock ur = new UseReentrantLock();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
ur.methos1();
ur.methos2();
}
},"t1");
t1.start();
}
}
执行结果:线程1执行完方法1释放锁后确实可以继续获得锁执行方法2,并没有被锁阻塞,这大概就是可重入的意思吧。
线程:t1获取了锁进入方法1
线程:t1释放锁退出方法1
线程:t1获取了锁进入方法2
线程:t1释放锁退出方法2
2、还记得我们在在使用synchronized的时候,如果需要多线程间进行协作工作则需要使用超类Object的wait()和notify()、notifyAll()方法进行配合工作。
那么同样的,我们在使用Lock的时候,可以使用一个新的等待/通知的类,它就是Conditon。这个Condition一定是针对具体某一把锁的,也就是在只有锁的基础上才会产生Condition。
下面看看例子:
public class UseReentrantLockCondition {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void method1(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+":获取锁进入方法1");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+":方法1进行等待状态,释放锁");
condition.await(); //效果等同于Object的await()方法,会释放锁
System.out.println("线程"+Thread.currentThread().getName()+":重新获取锁继续执行方法1");
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void method2(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"获取锁进入方法2");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+":方法2发出唤醒");
condition.signal(); //效果等同于Object的notify()方法,不会释放锁
System.out.println("线程"+Thread.currentThread().getName()+":方法2执行结束");
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
UseReentrantLockCondition ur = new UseReentrantLockCondition();
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
ur.method1();
}
},"t1");
Thread t2 = new Thread(new Runnable(){
@Override
public void run() {
ur.method2();
}
},"t2");
t1.start();
try {
Thread.sleep(1000); //保证线程1先执行
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
执行结果:可以看到,当线程1执行的方法1调用await方法后就释放锁了,然后线程2就获得了锁执行方法2,当方法2调用了唤醒操作signal而且执行完毕后(唤醒操作不释放锁),线程1再获得锁得以继续执行方法1。
线程t1:获取锁进入方法1
线程t1:方法1进行等待状态,释放锁
线程t2获取锁进入方法2
线程t2:方法2发出唤醒
线程t2:方法2执行结束
线程t1:重新获取锁继续执行方法1
3、上面是一个Condition,其实一个Lock对象是可以产生多个Condition进行多线程间的交互的,所以是非常的灵活,可以使得部分需要唤醒的线程被唤醒,而其他线程则继续等待通知。
下面直接上例子:
public class UseReentrantLockConditions {
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
public void method1(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"进入方法1,等待");
c1.await(); //调用c1的await方法
System.out.println("线程"+Thread.currentThread().getName()+"继续执行方法1");
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void method2(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"进入方法2,等待");
c1.await(); //调用c1的await方法
System.out.println("线程"+Thread.currentThread().getName()+"继续执行方法2");
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void method3(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"进入方法3,等待");
c2.await(); //调用c2的await方法
System.out.println("线程"+Thread.currentThread().getName()+"继续执行方法3");
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void method4(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"进入方法4,执行c1的唤醒操作");
c1.signalAll();; //调用c1的signalAll方法,唤醒全部线程
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void method5(){
try{
lock.lock();
System.out.println("线程"+Thread.currentThread().getName()+"进入方法5,执行c2的唤醒操作");
c2.signal(); //执行c2的signal
}catch(Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
UseReentrantLockConditions ur = new UseReentrantLockConditions();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
ur.method1();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
ur.method2();
}
},"t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
ur.method3();
}
},"t3");
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
ur.method4();
}
},"t4");
Thread t5 = new Thread(new Runnable() {
@Override
public void run() {
ur.method5();
}
},"t5");
t1.start();
t2.start();
t3.start();
try {
Thread.sleep(2000); //保证线程123先执行
} catch (InterruptedException e) {
e.printStackTrace();
}
t4.start();
try {
Thread.sleep(2000); //让两个唤醒操作效果更明显
} catch (InterruptedException e) {
e.printStackTrace();
}
t5.start();
}
}
执行结果:可看到当线程4执行c1的唤醒操作signalAll()后,线程1和线程2都可以继续执行方法了。而当线程5执行c2的唤醒操作signal()后,线程3可以继续执行方法。
线程t1进入方法1,等待
线程t2进入方法2,等待
线程t3进入方法3,等待
线程t4进入方法4,执行c1的唤醒操作
线程t1继续执行方法1
线程t2继续执行方法2
线程t5进入方法5,执行c2的唤醒操作
线程t3继续执行方法3
小结:
其实ReentrantLock还有很多方法,其中下面还有三个需要注意一下,但是就不做例子了。
1、lockInterruptibly()方法:获得锁,但是优先响应中断。就是如果线程调用了interrupt()这个中断方法,那么不会去获得锁。
2、tryLock():尝试获得锁,如果成功立即返回true,反之失败就返回false。该方法不会进行等待,立即返回值。
3、tryLock(long time,TimeUnit unit):在给定时间内尝试获得锁。