ReentrantLock被称为显示锁,synchronized被称为隐式锁。
ReentrantLock和Condition是在java 1.5中才出现的,它们用来替代传统的synchronized 、wait()、notify()来实现线程间的协作,相比使用synchronized,使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。
ReentrantLock
ReentrantLock实现了Lock接口。
用 ReentrantLock 保护代码块的基本结构如下:
myLock.lock(); // a ReentrantLock object
try
{
critical section //临界区代码
}
finally
{
myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
}
这一结构确保任何时刻只有一个线程进人临界区。一旦一个线程封锁了锁对象, 其他任何线程都无法通过 lock 语句。当其他线程调用 lock 时,它们被阻塞,直到第一个线程释放锁对象。
Condition
- Condition是个接口,基本的方法就是await()和signal()方法;
- Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
- 调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
- Condition中的await()对应Object的wait();
Condition中的signal()对应Object的notify();
Condition中的signalAll()对应Object的notifyAll()。
ReentrantLock 和 synchronized都是可重入锁
可重入锁也叫递归锁,同一线程在外层方法获取锁的时候,再进入内层方法会自动获取锁,也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
看下例子就很好理解:
(1). 基于 synchronized
public class ReentrantLockTest {
public static void main(String[] args) {
Phone phone = new Phone();
// 第一个线程
new Thread(() -> {
phone.sendMsg();
}, "t1").start();
// 第二个线程
new Thread(() -> {
phone.sendMsg();
}, "t2").start();
}
}
class Phone {
// 发送短信同步方法
public synchronized void sendMsg() {
System.out.println(Thread.currentThread().getName() + " called sendMsg()");
// 进入另外一个同步着的方法
sendEmail();
}
// 发送邮件同步方法
public synchronized void sendEmail() {
System.out.println(Thread.currentThread().getName() + " ******called sendEmail()");
}
}
运行结果:
t1 called sendMsg() // t1线程在外层方法获取锁的时候
t1 ******called sendEmail() // t1再进入内层方法会自动获取锁
t2 called sendMsg()
t2 ******called sendEmail()
(2). 基于 ReentrantLock
public class ReentrantLockTest {
public static void main(String[] args) {
Phone phone = new Phone();
// 第一个线程
Thread t1 = new Thread(phone, "t1");
// 第二个线程
Thread t2 = new Thread(phone, "t2");
t1.start();
t2.start();
}
}
class Phone implements Runnable {
ReentrantLock lock = new ReentrantLock();
// 发送短信方法
public void sendMsg() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " called sendMsg()");
// 进入另一个方法
sendEmail();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// 发送邮件方法
public void sendEmail() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " ******called sendEmail()");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
@Override
public void run() {
sendMsg();
}
}
运行结果:
t1 called sendMsg() // t1线程在外层方法获取锁的时候
t1 ******called sendEmail() // t1再进入内层方法会自动获取锁
t2 called sendMsg()
t2 ******called sendEmail()