java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。

一、简介

ReentrantLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。

  • synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
  • synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
  • synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断。

ReentrantLock好像比synchronized关键字没好太多,我们再去看看synchronized所没有的,一个最主要的就是ReentrantLock还可以实现公平锁机制。什么叫公平锁呢?也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。

二、使用

1、简单使用

代码:

  1. import java.util.concurrent.TimeUnit;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * @description 可重入锁测试
  6. * @author hzx
  7. * @date 2022-04-01
  8. */
  9. public class ReentrantLockTest {
  10. private static final Lock lock = new ReentrantLock();
  11. public static void main(String[] args) {
  12. new Thread(() -> test(),"线程A").start();
  13. new Thread(() -> test(),"线程B").start();
  14. }
  15. public static void test() {
  16. try {
  17. lock.lock();
  18. System.out.println(Thread.currentThread().getName()+"获取了锁");
  19. TimeUnit.SECONDS.sleep(1);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. } finally {
  23. System.out.println(Thread.currentThread().getName()+"释放了锁");
  24. lock.unlock();
  25. }
  26. }
  27. }

执行结果:

  1. 线程A获取了锁
  2. 线程A释放了锁
  3. 线程B获取了锁
  4. 线程B释放了锁

在这里我们定义了一个ReentrantLock,然后再test方法中分别lock和unlock,运行一边就可以实现我们的功能。这就是最简单的功能实现,代码很简单。我们再看看ReentrantLock和synchronized不一样的地方,那就是公平锁的实现。

2、公平锁实现

首先new一个ReentrantLock的时候参数为true,表明实现公平锁机制。公平锁的含义就是谁等的时间最长,谁就先获取锁。在这里我们多定义几个线程,然后在test方法中循环执行了两次加锁和解锁的过程。

代码:

  1. import java.util.concurrent.TimeUnit;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * @description 可重入锁测试(公平锁实现)
  6. * @author hzx
  7. * @date 2022-04-01
  8. */
  9. public class ReentrantLockTest2 {
  10. private static final Lock lock = new ReentrantLock(true);
  11. public static void main(String[] args) {
  12. new Thread(() -> test(),"线程1").start();
  13. new Thread(() -> test(),"线程2").start();
  14. new Thread(() -> test(),"线程3").start();
  15. new Thread(() -> test(),"线程4").start();
  16. new Thread(() -> test(),"线程5").start();
  17. }
  18. public static void test() {
  19. for (int i = 0; i < 2; i++) {
  20. try {
  21. lock.lock();
  22. System.out.println(Thread.currentThread().getName()+"获取了锁");
  23. TimeUnit.SECONDS.sleep(1);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. } finally {
  27. lock.unlock();
  28. }
  29. }
  30. }
  31. }

执行结果:

  1. 线程1获取了锁
  2. 线程2获取了锁
  3. 线程3获取了锁
  4. 线程4获取了锁
  5. 线程5获取了锁
  6. 线程1获取了锁
  7. 线程2获取了锁
  8. 线程3获取了锁
  9. 线程4获取了锁
  10. 线程5获取了锁
3、非公平锁实现

非公平锁那就随机的获取,谁运气好,cpu时间片轮到哪个线程,哪个线程就能获取锁,和上面公平锁的区别很简单,就在于先new一个ReentrantLock的时候参数为false,当然我们也可以不写,默认就是false。

代码:

  1. import java.util.concurrent.TimeUnit;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. /**
  5. * @description 可重入锁测试(非公平锁实现)
  6. * @author hzx
  7. * @date 2022-04-01
  8. */
  9. public class ReentrantLockTest3 {
  10. private static final Lock lock = new ReentrantLock(false);
  11. public static void main(String[] args) {
  12. new Thread(() -> test(),"线程1").start();
  13. new Thread(() -> test(),"线程2").start();
  14. new Thread(() -> test(),"线程3").start();
  15. new Thread(() -> test(),"线程4").start();
  16. new Thread(() -> test(),"线程5").start();
  17. }
  18. public static void test() {
  19. for (int i = 0; i < 2; i++) {
  20. try {
  21. lock.lock();
  22. System.out.println(Thread.currentThread().getName()+"获取了锁");
  23. TimeUnit.MILLISECONDS.sleep(1);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. } finally {
  27. lock.unlock();
  28. }
  29. }
  30. }
  31. }

执行结果:

  1. 线程1获取了锁
  2. 线程2获取了锁
  3. 线程2获取了锁
  4. 线程3获取了锁
  5. 线程1获取了锁
  6. 线程3获取了锁
  7. 线程4获取了锁
  8. 线程4获取了锁
  9. 线程5获取了锁
  10. 线程5获取了锁

整个过程是随机的,没有固定的先后顺序,亲自测试时可以将for循环的次数增大,可以明显看出效果。

【多线程】可重入锁 ReentrantLock的更多相关文章

  1. Java多线程——深入重入锁ReentrantLock

    简述 ReentrantLock 是一个可重入的互斥(/独占)锁,又称为“独占锁”. ReentrantLock通过自定义队列同步器(AQS-AbstractQueuedSychronized,是实现 ...

  2. synchronized关键字,Lock接口以及可重入锁ReentrantLock

    多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修 ...

  3. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  4. 轻松学习java可重入锁(ReentrantLock)的实现原理

    转载自https://blog.csdn.net/yanyan19880509/article/details/52345422,(做了一些补充) 前言 相信学过java的人都知道 synchroni ...

  5. java 可重入锁ReentrantLock的介绍

    一个小例子帮助理解(我们常用的synchronized也是可重入锁) 话说从前有一个村子,在这个村子中有一口水井,家家户户都需要到这口井里打水喝.由于井水有限,大家只能依次打水.为了实现家家有水喝,户 ...

  6. 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)

    前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...

  7. 17_重入锁ReentrantLock

    [概述] 重入锁可以完全代替synchronized关键字. 与synchronized相比,重入锁ReentrantLock有着显示的操作过程,即开发人员必须手动指定何时加锁,何时释放锁,所以重入锁 ...

  8. Java 显示锁 之 重入锁 ReentrantLock(七)

    ReentrantLock 重入锁简介 重入锁 ReentrantLock,顾名思义,就是支持同一个线程对资源的重复加锁.另外,该锁还支持获取锁时的公平与非公平性的选择. 重入锁 ReentrantL ...

  9. 多线程通信的两种方式? (可重入锁ReentrantLock和Object)

    (一)Java中线程协作的最常见的两种方式: (1)利用Object的wait().notify()和notifyAll()方法及synchronized (2)使用Condition.Reentra ...

  10. Java多线程系列——深入重入锁ReentrantLock

    简述 ReentrantLock 是一个可重入的互斥(/独占)锁,又称为“独占锁”. ReentrantLock通过自定义队列同步器(AQS-AbstractQueuedSychronized,是实现 ...

随机推荐

  1. 阿里云上安装 Ubuntu + MariaDB

    阿里云上安装 Ubuntu + MariaDB 任务 安装第二个磁盘 设置第二个磁盘为数据盘 安装 MariaDB 配置 MariaDB 的数据文件目录 配置 MariaDB 远程访问 安装第二个磁盘 ...

  2. 浅谈Nodejs应用的主文件index.js的组成部分

    前言 Node妹子的问世,着实让我们前端攻城狮兴奋了一把,尤其本屌听说Javascript可以写服务端后,兴奋的像是看到了二次元萝莉的胖子...(●'◡'●).呃哼...YY先到这里,原谅本屌是个二次 ...

  3. canvas实现平铺水印

    欲实现的水印平铺的效果图如下: 从图上看,应该做到以下几点: 文字在X和Y方向上进行平铺: 文字进行了一定的角度的旋转: 水印作为背景,其z-index位置应位于页面内容底部, 即不能覆盖页面主内容: ...

  4. 第一天·浏览器内核及Web标准

    一·浏览器及浏览器内核 1.常见的浏览器 (1)IE浏览器 IE是微软公司旗下浏览器,是目国内用户量最多的浏览器.IE诞生于1994年,当时微软为了对抗市场份额占据将近百分之九十的网景Netscape ...

  5. hbase增删查

    代码: package cn.idcast.hbase; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.h ...

  6. jsp+servlet上传excel并将数据导入到数据库表的实现方法

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  7. Android开发小经验

    1. TextView中的getTextSize返回值是以像素(px)为单位的, 而setTextSize()是以sp为单位的. 所以如果直接用返回的值来设置会出错,解决办法是 用setTextSiz ...

  8. mybatisPlus crud操作注意事项

    1.调用IService里的update方法,如果是自定义根据除主键外其它字段更新的时候,如果给主键id设置其它值不会更新主键id,如果未设置主键id值或者设置为null,同样不会更新主键id. 2. ...

  9. C# 中托管内存与非托管内存之间的转换

    c#有自己的内存回收机制,所以在c#中我们可以只new,不用关心怎样delete,c#使用gc来清理内存,这部分内存就是managed memory,大部分时候我们工作于c#环境中,都是在使用托管内存 ...

  10. 2021.11.02 eleveni的水省选题的记录

    2021.11.02 eleveni的水省选题的记录 因为eleveni比较菜,所以eleveni决定从绿题开始水 --实际上菜菜的eleveni连绿题都不一定能水过/忍不住哭了 [P2217 HAO ...