一、特点
- 1.可中断:线程2可以让线程1获取的锁取消掉。
- 2.可以设置超时时间:规定时间内,线程1争抢不了锁,可以放弃锁的竞争。
- 3.可以设置为公平锁:竞争不到锁的线程在等待过程中,当锁释放后,谁先等待的谁先得到锁(先进先出);如果随机去争抢,线程太多可能有些线程始终抢不到锁,会造成线程饥饿。
- 4.支持多个条件变量:synchronized中对象的monitor有个waitset,ReentrantLock支持多个waitset。
- 5.可重入:同一个线程获得了锁,未释放锁,可以再次获得这把锁。
二、代码示例
2.1 可重入
package com.learning.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author wangyouhui
* @Description ReentrantLock 可重入
**/
@Slf4j
public class ReentrantLockLearning {
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();
}
}
}
2.2 可打断
- 1.lock方法是不能被打断的,使用lockInterruptibly方法支持可打断
- 2.如果没有竞争,lockInterruptibly方法会获取对象锁
- 3.如果有竞争,则进入阻塞队列,可以被其它线程用interrupt方法打断
- 4.使用线程的interrupt方法即可打断
- 5.可以使用打断来防止线程无限制的等待下去,可以避免死锁的发生
package com.learning.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author wangyouhui
* @Description ReentrantLock 可打断
**/
@Slf4j
public class ReentrantLockInterruptLearning {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
log.info("尝试获得锁");
reentrantLock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.info("线程1未获取到锁,等待中被打断");
return;
}
try {
log.info("线程1获取到锁");
}finally {
// 释放锁
reentrantLock.unlock();
}
}, "thread1");
// 主线程获得锁
log.info("主线程获得锁");
reentrantLock.lock();
thread1.start();
log.info("线程1未获得锁在等待");
Thread.sleep(2000);
log.info("主线程打断线程1");
thread1.interrupt();
}
}
2.3 可超时
- 1.tryLock可以尝试获取锁,可以在指定时间内获取锁
- 2.tryLock可以被其它线程打断
- 3.其它线程获取到锁,然后释放锁,只要时间小于tryLock的时间,尝试获取锁还是可以成功的
package com.learning.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author wangyouhui
* @Description 锁超时
**/
@Slf4j
public class ReentrantLockTimeoutLearning {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()->{
log.info("线程1尝试获取锁");
try {
if (!reentrantLock.tryLock(2, TimeUnit.SECONDS)) {
log.info("线程1没有获取到锁");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
log.info("线程1没有获取到锁");
return;
}
try {
log.info("线程1获取到锁");
}finally{
log.info("线程1释放锁");
reentrantLock.unlock();
}
}, "thread1");
log.info("主线程先获得锁");
reentrantLock.lock();
thread1.start();
Thread.sleep(1000);
log.info("主线程释放锁");
reentrantLock.unlock();
}
}
2.4 公平锁
- 1.默认是不公平锁
- 2.先加锁的优先获得cpu的时间片
- 3.一般没有必要设置为公平锁,会降低并发度
package com.learning.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockFair {
private static Lock lock = new ReentrantLock(true); // true表示公平锁
private static Condition condition = lock.newCondition();
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获得了锁");
condition.await();
System.out.println(Thread.currentThread().getName() + "被唤醒了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "线程" + i).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
condition.signalAll();
System.out.println("唤醒所有线程");
} finally {
lock.unlock();
}
}
}
2.5 条件变量
- 1.synchronized中的条件变量waitSet,当条件不满足时,线程进入waitSet等待
- 2.ReentrantLock的条件变量支持多个,synchronized不满足条件的线程都在同一个waitSet中等待,ReentrantLock可以让不同条件不满足的线程在不同的地方等待
package com.learning.lock;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class ReentrantLockCondition {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition1 = lock.newCondition();
private static Condition condition2 = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
lock.lock();
log.info("线程1获取到锁");
try {
log.info("线程1获取到锁不满足条件而等待");
condition1.await();
log.info("线程1满足条件继续执行");
} finally {
lock.unlock();
log.info("线程1释放锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程1");
thread1.start();
Thread thread2 = new Thread(() -> {
try {
lock.lock();
log.info("线程2获取到锁");
try {
log.info("线程2获取到锁不满足条件而等待");
condition2.await();
log.info("线程2满足条件继续执行");
} finally {
lock.unlock();
log.info("线程2释放锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程2");
thread2.start();
Thread.sleep(1000);
lock.lock();
try {
log.info("条件1满足,唤醒线程1");
condition1.signal();
} finally {
lock.unlock();
}
lock.lock();
try {
log.info("条件2满足,唤醒线程2");
condition2.signal();
} finally {
lock.unlock();
}
}
}
