locklock水杯 locklock

ReentrantLock
ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized *** 和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大 。
ReentrantLock基本用法
先来看一下ReentrantLock的基本用法:
public class ThreadDomain38{ private Lock lock = new ReentrantLock();public void testMethod() { 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 extends Thread{ private ThreadDomain38 td;public MyThread38(ThreadDomain38 td) { this.td = td; }public void run() { td.testMethod(); }}public static void main(String[] args){ ThreadDomain38 td = new ThreadDomain38(); MyThread38 mt0 = new MyThread38(td); MyThread38 mt1 = new MyThread38(td); MyThread38 mt2 = new MyThread38(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 class ThreadDomain39{ private Lock lock = new ReentrantLock();public void methodA() { 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 void methodB() { 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 extends Thread{ private ThreadDomain39 td;public MyThread39_0(ThreadDomain39 td) { this.td = td; }public void run() { td.methodA(); }}public class MyThread39_1 extends Thread{ private ThreadDomain39 td;public MyThread39_1(ThreadDomain39 td) { this.td = td; }public void run() { td.methodB(); }}写一个main函数启动这两个线程:
public static void main(String[] args){ ThreadDomain39 td = new ThreadDomain39(); MyThread39_0 mt0 = new MyThread39_0(td); MyThread39_1 mt1 = new MyThread39_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可以实现有选择性地通知,这是非常重要的
【locklock水杯locklock】看一下利用Condition实现等待/通知模型的最简单用法,下面的代码注意一下,await()和signal()之前,必须要先lock()获得锁,使用完毕在finally中unlock()释放锁,这和wait()/notify()/notifyAll()使用前必须先获得对象锁是一样的:
public class ThreadDomain40{ private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition();public void await() { try { lock.lock(); System.out.println("await时间为:" + System.currentTimeMillis()); condition.await(); System.out.println("await等待结束"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }public void signal() { try { lock.lock(); System.out.println("signal时间为:" + System.currentTimeMillis()); condition.signal(); } finally { lock.unlock(); } }}public class MyThread40 extends Thread{ private ThreadDomain40 td;public MyThread40(ThreadDomain40 td) { this.td = td; }public void run() { td.await(); }}public static void main(String[] args) throws Exception{ ThreadDomain40 td = new ThreadDomain40(); MyThread40 mt = new MyThread40(td); mt.start(); Thread.sleep(3000); td.signal();}看一下运行结果:

推荐阅读