一、什么是ReentrantLock
ReentrantLock是一种基于AQS框架的应用实现,是JDK中的一种线程并发访问的一种手段,它的功能类似与Synchronized,但是又不等于Synchronized,是一种互斥锁。
1.1 ReentrantLock与Synchronized的区别
1.2 reentrantLock比较Sysnchronized具有一下特点
二、ReentrantLock使用
2.1 常用的方法
2.2 基本用法
private static ReentrantLock reentrantLock = new ReentrantLock();
private void test1() {
//加锁,如果获取不到锁,就一直等待,直到获取到锁
reentrantLock.lock();
try {
System.out.println("业务逻辑开始执行");
Thread.sleep(3000);
System.out.println("业务逻辑花了三秒执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
System.out.println("释放锁,并且unlock()要放在最上面");
}
}
public static void main(String[] args) {
ReentrantLockTest test = new ReentrantLockTest();
test.test1();
}
2.3 可重入锁
可重入锁是指同一个线程如果首次获得这把锁,那么因为它是这把锁的拥有者,因此有权利再次获得这把锁。如果是不可重入锁,那么第二次获得这把锁时,自己也会被锁住。
private static void test2() {
reentrantLock.lock();
try {
System.out.println("执行有锁的test2方法.......");
test3();
} catch (Exception e) {
} finally {
reentrantLock.unlock();
}
}
private static void test3() {
System.out.println("普通方法test3......");
}
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
reentrantLock.lock();
try {
System.out.println("开始执行业务逻辑");
test2();
System.out.println("业务逻辑执行结束");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
2.4 可中断
synchronized和reentrantLock.lock()的锁,是不可被打断的,也就是说,别的线程已经获得了锁,线程就需要一直等待下去,不能中断,直到获得到锁才能运行
通过reenttrantLock.lockInterruptibly();可以通过调用阻塞线程的interrupt方法打断
private static void test6() throws InterruptedException {
new Thread(()->{
reentrantLock.lock();
try {
System.out.println("t2获取锁以后开始执行业务逻辑");
Thread.sleep(2000);
System.out.println("t2两秒以后线程起来");
} catch (Exception e) {
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
},"t2").start();
//确保t2先拿到锁
Thread.sleep(2000);
Thread t1 = new Thread(()->{
try {
reentrantLock.lockInterruptibly();
System.out.println("t1获取到锁");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("t1被打断......");
}finally {
reentrantLock.unlock();
}
},"t1");
t1.start();
System.out.println("打断t1");
t1.interrupt();
}
2.5 可设置超时时间
使用lock.trylock()方法会返回获取锁是否成功,如果成功,则返回true,反正则返回false。并且trylock方法可以设置指定的等待时间,在获取锁的过程中,如果等待时间超时,或者被打断,那么直接从阻塞队列中移除,此时获取锁就失败了,不会一直阻塞(可以用来实现解决死锁问题),如果不设置等待时间,会立即生效。
线程池工具类:线程池工具类_java-zh的博客-CSDN博客
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
test5();
}
private static void test5() {
ThreadPoolUtils.execute(() -> {
System.out.println("开始尝试获取锁....");
System.out.println("是否有获取锁 -> " + reentrantLock.tryLock());
try {
if (reentrantLock.tryLock(1, TimeUnit.SECONDS)) {
System.out.println("获取锁以后开始执行业务逻辑....");
} else {
System.out.println("设置了一秒,获取不到锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//主线程先获取锁
reentrantLock.lock();
try {
System.out.println("主业务逻辑开始执行");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
System.out.println("最后释放锁");
}
}
主业务逻辑开始执行
最后释放锁
开始尝试获取锁....
是否有获取锁 -> true
获取锁以后开始执行业务逻辑....
2.6 公平锁和非公平锁
//非公平锁(无参默认是非公平锁)
private static ReentrantLock reentrantLock = new ReentrantLock(false);
//公平锁
private static final ReentrantLock reentrantLock = new ReentrantLock(true);
线程2比线程1更先获取到锁
2.7 条件变量Condition
传统对象等待集合只有waitSet,Lock可以通过newCondtion()方法生成多个等待集合Condition对象。Lock和Condition是一对多关系。
2.7.1 使用流程
public class ConditionTest {
//ReentrantLock可以设置多个条件变量
private static boolean hasCar = false;
private static boolean hasTrain = false;
private static final ReentrantLock reentrantLock = new ReentrantLock();
//等待汽车的条件变量
private static Condition waitCarSet = reentrantLock.newCondition();
//等待火车条件变量
private static Condition waitTrainSet = reentrantLock.newCondition();
public static void main(String[] args) throws InterruptedException {
//等待汽车
ThreadPoolUtils.execute(ConditionTest::waitCar);
//等待火车
ThreadPoolUtils.execute(ConditionTest::waitTrain);
Thread.sleep(2000);
//汽车来了
ThreadPoolUtils.execute(ConditionTest::car);
//火车来了
ThreadPoolUtils.execute(ConditionTest::train);
}
private static void waitCar() {
reentrantLock.lock();
try {
System.out.println("汽车来了吗?");
while (!hasCar) {
System.out.println("汽车没来,再等等");
//进入等待汽车亭
waitCarSet.await();
}
System.out.println("汽车来了,开始准备上汽车了!");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
private static void waitTrain() {
reentrantLock.lock();
try {
System.out.println("火车到了吗?");
while (!hasTrain) {
System.out.println("火车还没有到");
waitTrainSet.await();
}
System.out.println("火车到了,开始准备上火车了!");
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
private static void train() {
reentrantLock.lock();
try {
System.out.println("火车来了");
hasTrain = true;
waitTrainSet.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
private static void car() {
reentrantLock.lock();
try {
System.out.println("汽车来了");
hasCar = true;
waitCarSet.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}
结果
汽车来了吗?
汽车没来,再等等
火车到了吗?
火车还没有到
汽车来了
汽车来了,开始准备上汽车了!
火车来了
火车到了,开始准备上火车了!
2.8 固定顺序运行
public class WaitTest {
private static final Object lock = new Object();
private static boolean flagRunnable = false;
public static void main(String[] args) {
ThreadPoolUtils.execute(() -> {
synchronized (lock) {
while (!flagRunnable) {
try {
//开始等待,等待的过程会释放锁
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("1");
}
});
ThreadPoolUtils.execute(() -> {
synchronized (lock) {
System.out.println("2");
flagRunnable = true;
lock.notify();
}
});
}
}
2.9 交替输出
线程1输出1五次,线程2输出2五次,线程3输出3五次,最后结果abcabcabcabcabc
2.9.1 synchronized交替输出
public class JiaoTiTest {
//需要输出的次数
private Integer num = 5;
public static void main(String[] args) {
JiaoTi jiaoTi = new JiaoTi(1,5);
ThreadPoolUtils.execute(()->{
jiaoTi.t("a",1,2);
});
ThreadPoolUtils.execute(()->{
jiaoTi.t("b",2,3);
});
ThreadPoolUtils.execute(()->{
jiaoTi.t("c",3,1);
});
}
}
class JiaoTi{
//当前的标记
private Integer flag;
//循环的次数
private Integer num;
public JiaoTi(Integer flag, Integer num) {
this.flag = flag;
this.num = num;
}
/**
*
* @param con 内容
* @param waitflag 当前标记
* @param nextFlag 下一个标记
* 比如当前内容为a 当前标记 1 下一个标记2
* 当前内容为b 当前标记2 下一个标记3
* 当前内容为c 当前标记3 下一个标记1
*/
public void t(String con, Integer waitflag, Integer nextFlag){
for (int i = 0; i < num; i++) {
synchronized (this){
//如果你要输出的标记跟抢占到资源的标记不一样,那么就进行等待
while (!flag.equals(waitflag)){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print(con);
//执行完成以后,当前的标记变成了下一个标记
flag = nextFlag;
this.notifyAll();
}
}
}
}
abcabcabcabcabc
2.9.2 reentrantLock交替输出
public class JiaoTiTest2 {
private static ReentrantLock reentrantLock = new ReentrantLock();
private static Condition condition1 = reentrantLock.newCondition();
private static Condition condition2 = reentrantLock.newCondition();
private static Condition condition3 = reentrantLock.newCondition();
public static void main(String[] args) {
ThreadPoolUtils.execute(()->{
noFair("a",condition1,condition2);
});
ThreadPoolUtils.execute(()->{
noFair("b",condition2,condition3);
});
ThreadPoolUtils.execute(()->{
noFair("c",condition3,condition1);
});
System.out.print("开始打印:");
reentrantLock.lock();
try {
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
public static void noFair(String con,Condition condition,Condition nextCondition){
for (int i = 0; i < 5; i++) {
reentrantLock.lock();
try {
condition.await();
System.out.print(con);
nextCondition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
}
}
开始打印:abcabcabcabcabc