一、什么是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
06-14 20:28