1. ReentrantLock可重入锁
相对于 synchronized 它具备如下特点:
- 可中断
- 可以设置超时时间
- 可以设置为公平锁
- 支持多个条件变量(相当于有多个EntryList)
- 与 synchronized 一样,都支持可重入
基本语法:
// 获取锁
reentrantLock.lock();
try {
// 临界区
} finally {
// 释放锁
reentrantLock.unlock();
}
1.1 可重入
- 可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。
- 如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住。
示例代码:
package tian;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j(topic = "c.Test22")
public class Test22 {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try {
log.debug("Enter Main");
m1();
} catch (Exception e) {
lock.unlock();
}
}
public static void m1() {
lock.lock();
try {
log.debug("Enter m1");
m2();
} catch (Exception e) {
lock.unlock();
}
}
public static void m2() {
lock.lock();
try {
log.debug("Enter m2");
} catch (Exception e) {
lock.unlock();
}
}
}
运行结果:
1.2 可打断
- 对象在获得锁后,可以被打断
- ReentrantLock对象的lockInterruptibly()方法可以被打断,ReentrantLock对象的lock方法不可被打断。
- 被动打断线程(其他线程调用interrupt方法)
示例代码:
@Slf4j(topic = "c.Test22")
public class Test22 {
private static final ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("启动...");
try {
// ReentrantLock对象的lockInterruptibly()方法可以被打断
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("等锁的过程中被打断");
return;
}
try {
log.debug("获得了锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
log.debug("获得了锁");
t1.start();
try {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt();
log.debug("执行打断");
} finally {
lock.unlock();
}
}
运行结果:
1.3 锁超时
lock.tryLock(2, TimeUnit.SECONDS)方法可以设置尝试获取锁的最大等待时间,如果超过了最大等待时间,则获取不到锁。这个方法返回一个布尔值,如果或得到了锁,则返回true否则返回false。如果这个方法不加参数,那么会一直尝试去获取锁。
1.3.1 示例代码(t1线程获取不到锁):
代码:
package tian;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j(topic = "c.Test22")
public class Test22 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("尝试获得锁");
try {
if (!lock.tryLock(2, TimeUnit.SECONDS)) {
log.debug("获取不到锁");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("获取不到锁");
return;
}
try {
log.debug("获得到锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
log.debug("获得到锁");
t1.start();
Thread.sleep(3000);
log.debug("释放了锁");
lock.unlock();
}
}
运行结果:
1.3.2 示例代码(t1线程获取到锁):
代码:
运行结果:
1.4 锁超时-解决哲学家就餐
代码:
package tian;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j(topic = "c.Test23")
public class Test23 {
public static void main(String[] args) {
Chopstick c1 = new Chopstick("1");
Chopstick c2 = new Chopstick("2");
Chopstick c3 = new Chopstick("3");
Chopstick c4 = new Chopstick("4");
Chopstick c5 = new Chopstick("5");
new Philosopher("苏格拉底", c1, c2).start();
new Philosopher("柏拉图", c2, c3).start();
new Philosopher("亚里士多德", c3, c4).start();
new Philosopher("赫拉克利特", c4, c5).start();
new Philosopher("阿基米德", c5, c1).start();
}
}
@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread {
Chopstick left;
Chopstick right;
public Philosopher(String name, Chopstick left, Chopstick right) {
super(name);
this.left = left;
this.right = right;
}
@Override
public void run() {
while (true) {
// 尝试获得左手筷子
if (left.tryLock()) {
try {
// 尝试获得右手筷子
if (right.tryLock()) {
try {
eat();
} finally {
right.unlock();
}
}
} finally {
// 如果没有获得右手的筷子 则释放自己手里的筷子 这里是解决哲学家就餐问题的关键
left.unlock();
}
}
}
}
private void eat() {
log.debug("eating...");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Chopstick extends ReentrantLock {
String name;
public Chopstick(String name) {
this.name = name;
}
@Override
public String toString() {
return "筷子{" + name + '}';
}
}
运行结果: