java lock condition_Java多线程11:ReentrantLock的使用和Condition

ReentrantLock

ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

ReentrantLock基本用法

先来看一下ReentrantLock的基本用法:

public classThreadDomain38

{private Lock lock = newReentrantLock();public voidtestMethod()

{try{

lock.lock();for (int i = 0; i < 2; i++)

{

System.out.println("ThreadName = " + Thread.currentThread().getName() +

", i = " +i);

}

}finally{

lock.unlock();

}

}

}

public class MyThread38 extendsThread

{privateThreadDomain38 td;publicMyThread38(ThreadDomain38 td)

{this.td =td;

}public voidrun()

{

td.testMethod();

}

}

public static voidmain(String[] args)

{

ThreadDomain38 td= newThreadDomain38();

MyThread38 mt0= newMyThread38(td);

MyThread38 mt1= newMyThread38(td);

MyThread38 mt2= newMyThread38(td);

mt0.start();

mt1.start();

mt2.start();

}

看一下运行结果:

ThreadName = Thread-1, i = 0ThreadName= Thread-1, i = 1ThreadName= Thread-0, i = 0ThreadName= Thread-0, i = 1ThreadName= Thread-2, i = 0ThreadName= Thread-2, i = 1

没有任何的交替,数据都是分组打印的,说明了一个线程打印完毕之后下一个线程才可以获得锁去打印数据,这也证明了ReentrantLock具有加锁的功能

ReentrantLock持有的是对象监视器

前面已经证明了ReentrantLock具有加锁功能,但我们还不知道ReentrantLock持有的是什么锁,因此写个例子看一下:

public classThreadDomain39

{private Lock lock = newReentrantLock();public voidmethodA()

{try{

lock.lock();

System.out.println("MethodA begin ThreadName = " +Thread.currentThread().getName());

Thread.sleep(5000);

System.out.println("MethodA end ThreadName = " +Thread.currentThread().getName());

}catch(InterruptedException e)

{

e.printStackTrace();

}finally{

lock.unlock();

}

}public voidmethodB()

{lock.lock();

System.out.println("MethodB begin ThreadName = " +Thread.currentThread().getName());

System.out.println("MethodB begin ThreadName = " +Thread.currentThread().getName());lock.unlock();

}

}

写两个线程分别调用methodA()和methodB()方法:

public class MyThread39_0 extendsThread

{privateThreadDomain39 td;publicMyThread39_0(ThreadDomain39 td)

{this.td =td;

}public voidrun()

{

td.methodA();

}

}

public class MyThread39_1 extendsThread

{privateThreadDomain39 td;publicMyThread39_1(ThreadDomain39 td)

{this.td =td;

}public voidrun()

{

td.methodB();

}

}

写一个main函数启动这两个线程:

public static voidmain(String[] args)

{

ThreadDomain39 td= newThreadDomain39();

MyThread39_0 mt0= newMyThread39_0(td);

MyThread39_1 mt1= newMyThread39_1(td);

mt0.start();

mt1.start();

}

看一下运行结果:

MethodB begin ThreadName = Thread-1MethodB begin ThreadName= Thread-1MethodA begin ThreadName= Thread-0MethodA end ThreadName= Thread-0

看不见时间,不过第四确实是格了5秒左右才打印出来的。从结果来看,已经证明了ReentrantLock持有的是对象监视器,可以写一段代码进一步证明这一结论,即去掉methodB()内部和锁相关的代码,只留下两句打印语句:

MethodA begin ThreadName = Thread-0MethodB begin ThreadName= Thread-1MethodB begin ThreadName= Thread-1MethodA end ThreadName= Thread-0

看到交替打印了,进一步证明了ReentrantLock持有的是"对象监视器"的结论。

不过注意一点,ReentrantLock虽然持有对象监视器,但是和synchronized持有的对象监视器不是一个意思,虽然我也不清楚两个持有的对象监视器有什么区别,不过把methodB()方法用synchronized修饰,methodA()不变,两个方法还是异步运行的,所以就记一个结论吧----ReentrantLock和synchronized持有的对象监视器不同。

另外,千万别忘了,ReentrantLock持有的锁是需要手动去unlock()的

Condition

synchronized与wait()和nitofy()/notifyAll()方法相结合可以实现等待/通知模型,ReentrantLock同样可以,但是需要借助Condition,且Condition有更好的灵活性,具体体现在:

1、一个Lock里面可以创建多个Condition实例,实现多路通知

2、notify()方法进行通知时,被通知的线程时Java虚拟机随机选择的,但是ReentrantLock结合Condition可以实现有选择性地通知,这是非常重要的

看一下利用Condition实现等待/通知模型的最简单用法,下面的代码注意一下,await()和signal()之前,必须要先lock()获得锁,使用完毕在finally中unlock()释放锁,这和wait()/notify()/notifyAll()使用前必须先获得对象锁是一样的:

public classThreadDomain40

{private Lock lock = newReentrantLock();private Condition condition =lock.newCondition();public voidawait()

{try{

lock.lock();

System.out.println("await时间为:" +System.currentTimeMillis());

condition.await();

System.out.println("await等待结束");

}catch(InterruptedException e)

{

e.printStackTrace();

}finally{

lock.unlock();

}

}public voidsignal()

{try{

lock.lock();

System.out.println("signal时间为:" +System.currentTimeMillis());

condition.signal();

}finally{

lock.unlock();

}

}

}

public class MyThread40 extendsThread

{privateThreadDomain40 td;publicMyThread40(ThreadDomain40 td)

{this.td =td;

}public voidrun()

{

td.await();

}

}

public static void main(String[] args) throwsException

{

ThreadDomain40 td= newThreadDomain40();

MyThread40 mt= newMyThread40(td);

mt.start();

Thread.sleep(3000);

td.signal();

}

看一下运行结果:

await时间为:1443970329524signal时间为:1443970332524await等待结束

差值是3000毫秒也就是3秒,符合代码预期,成功利用ReentrantLock的Condition实现了等待/通知模型。其实这个例子还证明了一点,Condition的await()方法是释放锁的,原因也很简单,要是await()方法不释放锁,那么signal()方法又怎么能调用到Condition的signal()方法呢?

注意要是用一个Condition的话,那么多个线程被该Condition给await()后,调用Condition的signalAll()方法唤醒的是所有的线程。如果想单独唤醒部分线程该怎么办呢?new出多个Condition就可以了,这样也有助于提升程序运行的效率。使用多个Condition的场景是很常见的,像ArrayBlockingQueue里就有。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值