前言:
synchronized和ReentrantLock同为锁机制,synchronized内置锁为同步锁的首选。而显示锁ReentrantLock,它并不是用来替代synchronized的,而是作为内置锁不适用时另一种可选择的高级功能。
什么情况下使用ReentrantLock,这里不列详细的原理,只列比较容易理解的地方:
1、竞争资源激烈时用ReentrantLock,反之用synchronized;
- 竞争资源不激烈的情况下,synchronized的性能较高,synchronized机制属于轻量级,对于只有少数线程中运行成本较低,而ReentrantLock相反。
- 竞争资源激烈的情况下,ReentrantLock的性能较高,因为synchronized属于线程独占锁,比如A已经获得了该锁,B也想获得该锁就只有依靠阻塞来等待,直到A成功释放掉该锁(容易出现死锁),此时还有个操作就是CPU不断的在A和B之间切换,一旦还有CDEF等等若干个线程和B同一种情况,可想而知CPU在上下文切换上会浪费很多精力。而ReentrantLock锁的方式可以很好的优化这一问题。它的工作机制是如果A被锁住,BCDEF等线程试图获取该锁,一段时间后发现获取不到,它们可以中断自己,之后CPU不会再切换到这些线程而是专心去执行A的任务。所以在竞争激烈的情况下ReentrantLock更加有优势。(简单说就是JVM花更少的时间来调度,更多的时间来执行)
2、需要手动控制线程的中断、唤起等操作。
因为ReentrantLock为显示锁,即可调用lock()/unlock()等方法进行操作,虽跟synchronized内建型锁相比代码缺乏优雅性,但和Condition相互配合操作性更加灵活。
下面一个简单的Demo。
逻辑是实现一个简单的生产者和消费者模式的例子:
提一个简单的问题,让我们带着问题参考代码。
PS:这里为了代码的简介(懒),就拿Int型对象mQueue代替队列对象,请把mQueue想象成队列对象。。
启动程序
public static void main(String[] args) {
TestReentrantLock testReentrantLock = new TestReentrantLock();
testReentrantLock.startTest();
}
下面为TestReentrantLock.java代码
public class TestReentrantLock {
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
int mQueue = 0;
public void startTest() {
newThread();
producer();
}
public void newThread() {
new Thread(new Runnable() {
@Override
public void run() {
consumer();
}
}).start();
}
public void consumer() {
reentrantLock.lock();
if (reentrantLock.isLocked()){
System.out.println("\nConsumer gets the lock\n");
}
try {
while (mQueue == 0) {
System.out.println("The thread is waiting for consumers.\n");
condition.await();
System.out.println("The thread is starting for consumers.\n");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
if (!reentrantLock.isLocked()){
System.out.println("Consumer releases the lock\n");
}
}
System.out.println("Done");
}
public void producer() {
try {
Thread.sleep(1000);
reentrantLock.lock();
Thread.sleep(5000);
mQueue++;
System.out.println("Producer add content\n");
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
if (!reentrantLock.isLocked()){
System.out.println("Producer releases the lock\n");
}
}
}
}
1、创建一个线程,用ReentrantLock对象锁住,获取mQueue队列中的内容,如果有内容完成操作,如果没有内容则利用Condition对象中断线程,当Condition对象调用await()方法后会调用native方法自动解锁。
2、在新线程获取内容为空之后,主线程的producer()方法才执行,用ReentrantLock对象锁住,给mQueue添加内容,利用Condition对象调用signal()方法唤起新线程。
3、被唤起被中断的新线程,从被中断的代码继续向下执行,如果符合while()条件里面则重复执行其中的代码,不符合条件则跳出while()向下执行。