1.与synchronized不同点:
- 可中断
- 可以设置超时时间
- 可以设置公平锁,公平锁就是为了解决饥饿线程,让线程排队,先进先出,先来的线程先执行。
- 支持多个条件变量
2.与synchronized相同点都支持锁的可重入。
基本格式:
//获取锁 reentrantLock.lock(); try { } finally { //必须释放锁 reentrantLock.unlock(); }
可重入
同一个线程如果首次获得了这把锁,因为它是这把锁的拥有者,因此有权利再次获取这把锁。
如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住。
@Slf4j
public class ReentrantLockDemo {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
//获取锁
reentrantLock.lock();
try {
log.info("调用main方法...");
method1();
} finally {
//必须释放锁
reentrantLock.unlock();
}
}
public static void method1() {
reentrantLock.lock();
try {
log.info("调用method1方法...");
method2();
}finally {
reentrantLock.unlock();
}
}
public static void method2() {
reentrantLock.lock();
try {
log.info("调用method2方法...");
}finally {
reentrantLock.unlock();
}
}
}
可被中断
lockInterruptibly方法表示可被打断的锁
lokc方法加锁表示不可被打断的锁
可打断的锁,可以防止死锁。
@Slf4j
public class Reen {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
log.info("尝试获取锁....");
//lockInterruptibly可被打断的锁
reentrantLock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.info("等待获取锁的线程被打断....返回");
return;
}
try {
log.info("获取到了锁...");
}finally {
reentrantLock.unlock();
}
}, "t1");
//main线程获取锁
reentrantLock.lock();
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("打断线程t1....");
t1.interrupt();
}
}
可以设置超时时间
tryLock方法,设置超时时间,避免长时间等待,防止死锁。
tryLock方法不设置超时时间参数表示获取不到锁立刻返回。
@Slf4j
public class ReenDemo {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
//尝试获取锁,时间是立刻返回
log.info("尝试获取锁...");
try {
if(!reentrantLock.tryLock(2, TimeUnit.SECONDS)) {
log.info("尝试获取锁失败,返回...");
return;
}
log.info("尝试获取锁成功...");
} catch (InterruptedException e) {
e.printStackTrace();
log.info("尝试获取锁被打断,返回...");
return;
}
try {
}finally {
reentrantLock.unlock();
}
}, "t1");
reentrantLock.lock();
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
}finally {
reentrantLock.unlock();
}
}
}
解决哲学家吃饭的死锁问题:
public class DeadLockDemo {
public static void main(String[] args) {
Chopstick chopstick1 = new Chopstick("1");
Chopstick chopstick2 = new Chopstick("2");
Chopstick chopstick3 = new Chopstick("3");
Chopstick chopstick4 = new Chopstick("4");
Chopstick chopstick5 = new Chopstick("5");
new Philosopher("哲学家1", chopstick1, chopstick2).start();
new Philosopher("哲学家2", chopstick2, chopstick3).start();
new Philosopher("哲学家3", chopstick3, chopstick4).start();
new Philosopher("哲学家4", chopstick4, chopstick5).start();
new Philosopher("哲学家5", chopstick5, chopstick1).start();
}
}
@Slf4j
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) {//这里意思是一位哲学家获取了两根筷子后吃完饭,又重新开始下一轮...
//解决死锁问题,使用ReentrantLock.tryLock方法,避免长时间等待不释放锁的情况
if(left.tryLock()) {
try {
if(right.tryLock()) {
try {
eat();
}finally {
right.unlock();
}
}
}finally {
left.unlock();
}
}
// synchronized (left) {
// synchronized (right) {
// eat();
// }
// }
}
}
private void eat() {
log.info("eat...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 筷子类
*/
class Chopstick extends ReentrantLock{
//名称
private String name;
public Chopstick(String name) {
this.name = name;
}
@Override
public String toString() {
return "Chopstick{" +
"name='" + name + '\'' +
'}';
}
}
公平锁
ReentrantLock模式是非公平锁,synchronized也是非公平锁。但是ReentrantLock可以设置为公平锁。new ReentrantLock(true); 公平锁可以防止饥饿锁。按照先进先出的规则。公平锁会降低并发度。