1,介绍:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
在JAVA的多线程编程中,我们可以使用synchronized关键字来实现线程之间的同步互斥,但是JDK1.5中新增了ReentrantLock类同样也能达到效果,并且功能上更加强大。比如有嗅探锁定功能,多路分支通知功能,并且使用上比synchronized更加灵活。

2,基本使用:

使用lock()方法上锁,使用unlock()方法解锁
效果:
  • 加锁和解锁之间的代码块,多个线程运行变串行

代码示例:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* @ClassName LockReentrantLockExample
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/24.
*/
public class LockReentrantLockExample {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Runnable r = () -> {
String tName = Thread.currentThread().getName();
System.out.println("子线程运行");
lock.lock();
System.out.println(tName + "准备睡觉了");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tName + "睡醒了");
lock.unlock();
}; Thread thread = new Thread(r,"child1");
Thread thread1 = new Thread(r,"child2");
Thread thread2 = new Thread(r,"child3");
thread.start();
thread1.start();
thread2.start();
}
}

3、等待/通知 机制的实现:

3.1 Conditiond对象的await方法实现等待,signal方法实现通知
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* @ClassName LockReentrantLockCondition
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/25.
*/
public class LockReentrantLockCondition {
public static void main(String[] args) {
//锁
Lock lock = new ReentrantLock();
//暂停唤醒工具
Condition condition = lock.newCondition();
//子线程本地变量
ThreadLocal<String> tName = new ThreadLocal<>(){
@Override
protected String initialValue() {
return Thread.currentThread().getName();
}
};
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(tName.get() + "开始运行!");
lock.lock();
try {
Thread.sleep(1000);
System.out.println(tName.get() + "暂停");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tName.get() + "运行结束");
lock.unlock();
}
};
Thread thread = new Thread(r,"A");
Thread thread1 = new Thread(r,"B");
Thread thread2 = new Thread(r,"C");
Thread call = new Thread(){
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
condition.signalAll();
lock.unlock();
}
};
thread.start();
thread1.start();
thread2.start();
call.start(); }
}

效果:锁依次被ABC线程获取到,分别进入await()状态。最后call线程才获取到ABC抛出的锁,

3.2等待通知案例:做馒头,吃馒头,相对优化版

  • 做馒头多线程几乎并发,做馒头耗时并发sleep;
  • 吃馒头几乎并发,sleep 并发执行 lock.unlock();在 sleep外
import java.util.LinkedList;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* @ClassName ReentrantLockConditionMantou
* @projectName: object1
* @author: Zhangmingda
* @description: XXX
* date: 2021/4/25.
*/
public class ReentrantLockConditionMantou { private static void reentrantLockCondition(){
LinkedList<String> mantous = new LinkedList<>();
Lock lock = new ReentrantLock();
Condition fuwuyuan = lock.newCondition();
Runnable produce = () -> {
String threadName = Thread.currentThread().getName();
while (true) {
lock.lock();
if (mantous.size() >= 10 ){
fuwuyuan.signalAll(); //有可能被另一个厨师实例获取到
try {
Thread.sleep(100); //两个厨师,避免厨师之间相互获取到锁,给点时间让食客获取到锁
System.out.println(threadName + ": 馒头做够了.........................你们来吃吧");
fuwuyuan.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
}
else {
mantous.add("馒头");
System.out.println(threadName + ",我生产了一个馒头,当前的馒头数量是" + mantous.size());
lock.unlock();
try {
Thread.sleep(1000); //做馒头花时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable customer = () -> {
String threadName = Thread.currentThread().getName();
Random random = new Random();
while (true) {
//吃馒头的数量
int buyMantou = Math.abs(random.nextInt()) % 5 + 1;
//获取锁
lock.lock();
if (mantous.size() < buyMantou) {
fuwuyuan.signalAll();
System.out.println("\033[31;1m" + threadName + "想吃" + buyMantou + "只有" +mantous.size() + "个了,快做馒头吧\033[0m");
try {
//稍微歇一歇
Thread.sleep(300); //给点机会上厨师获取到锁,避免其他食客获取到锁
fuwuyuan.await();
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
} } else {
//吃馒头
for (int i = 0; i < buyMantou; i++) {
if(mantous.poll() == null){
System.out.println(threadName + "吃了个空.....");
}
}
System.out.println(threadName + ",我吃了" + buyMantou + "个馒头,当前馒头的数量是:" + mantous.size());
lock.unlock();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(produce,"厨师1");
Thread thread1 = new Thread(produce,"厨师2");
Thread thread2 = new Thread(customer,"饭桶1");
Thread thread3 = new Thread(customer,"饭桶2");
Thread thread4 = new Thread(customer,"饭桶3");
thread.start();
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
public static void main(String[] args) {
reentrantLockCondition();
}
}

"C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3\jbr\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3\lib\idea_rt.jar=54462:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3\bin" -Dfile.encoding=UTF-8 -classpath D:\JavaStudy2\object1\out\production\多线程 ReentrantLockConditionMantou
厨师1,我生产了一个馒头,当前的馒头数量是1
厨师2,我生产了一个馒头,当前的馒头数量是2
饭桶3,我吃了2个馒头,当前馒头的数量是:0
饭桶1想吃4只有0个了,快做馒头吧
饭桶2想吃3只有0个了,快做馒头吧
饭桶1想吃5只有0个了,快做馒头吧
饭桶2想吃4只有0个了,快做馒头吧
厨师1,我生产了一个馒头,当前的馒头数量是1
厨师2,我生产了一个馒头,当前的馒头数量是2
饭桶1想吃4只有2个了,快做馒头吧
饭桶2想吃5只有2个了,快做馒头吧
饭桶1,我吃了1个馒头,当前馒头的数量是:1
饭桶3,我吃了1个馒头,当前馒头的数量是:0
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
厨师1,我生产了一个馒头,当前的馒头数量是3
厨师2,我生产了一个馒头,当前的馒头数量是4
饭桶1,我吃了3个馒头,当前馒头的数量是:1
饭桶3想吃3只有1个了,快做馒头吧
饭桶2想吃3只有1个了,快做馒头吧
厨师1,我生产了一个馒头,当前的馒头数量是2
厨师2,我生产了一个馒头,当前的馒头数量是3
饭桶3想吃5只有3个了,快做馒头吧
饭桶2想吃4只有3个了,快做馒头吧
饭桶3,我吃了1个馒头,当前馒头的数量是:2
厨师1,我生产了一个馒头,当前的馒头数量是3
厨师2,我生产了一个馒头,当前的馒头数量是4
饭桶1,我吃了2个馒头,当前馒头的数量是:2
厨师1,我生产了一个馒头,当前的馒头数量是3
厨师2,我生产了一个馒头,当前的馒头数量是4
饭桶3想吃5只有4个了,快做馒头吧
饭桶2,我吃了4个馒头,当前馒头的数量是:0
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶1想吃4只有2个了,快做馒头吧
饭桶3想吃5只有2个了,快做馒头吧
饭桶1,我吃了2个馒头,当前馒头的数量是:0
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶2想吃5只有2个了,快做馒头吧
饭桶3想吃4只有2个了,快做馒头吧
厨师2,我生产了一个馒头,当前的馒头数量是3
厨师1,我生产了一个馒头,当前的馒头数量是4
饭桶2,我吃了1个馒头,当前馒头的数量是:3
饭桶1,我吃了3个馒头,当前馒头的数量是:0
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶2想吃5只有2个了,快做馒头吧
饭桶3想吃3只有2个了,快做馒头吧
厨师1,我生产了一个馒头,当前的馒头数量是3
厨师2,我生产了一个馒头,当前的馒头数量是4
饭桶2,我吃了1个馒头,当前馒头的数量是:3
饭桶1,我吃了3个馒头,当前馒头的数量是:0
厨师1,我生产了一个馒头,当前的馒头数量是1
厨师2,我生产了一个馒头,当前的馒头数量是2
厨师2,我生产了一个馒头,当前的馒头数量是3
饭桶2,我吃了1个馒头,当前馒头的数量是:2
厨师1,我生产了一个馒头,当前的馒头数量是3
饭桶1想吃5只有3个了,快做馒头吧
饭桶3,我吃了1个馒头,当前馒头的数量是:2
厨师1,我生产了一个馒头,当前的馒头数量是3
厨师2,我生产了一个馒头,当前的馒头数量是4
饭桶2,我吃了3个馒头,当前馒头的数量是:1
厨师2,我生产了一个馒头,当前的馒头数量是2
厨师1,我生产了一个馒头,当前的馒头数量是3
饭桶3,我吃了2个馒头,当前馒头的数量是:1
厨师1,我生产了一个馒头,当前的馒头数量是2
厨师2,我生产了一个馒头,当前的馒头数量是3
饭桶2,我吃了2个馒头,当前馒头的数量是:1
厨师1,我生产了一个馒头,当前的馒头数量是2
厨师2,我生产了一个馒头,当前的馒头数量是3
饭桶3想吃5只有3个了,快做馒头吧
饭桶1想吃5只有3个了,快做馒头吧
饭桶3,我吃了2个馒头,当前馒头的数量是:1
厨师1,我生产了一个馒头,当前的馒头数量是2
厨师2,我生产了一个馒头,当前的馒头数量是3
饭桶2想吃4只有3个了,快做馒头吧
饭桶1,我吃了3个馒头,当前馒头的数量是:0
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶3想吃5只有2个了,快做馒头吧
饭桶2,我吃了1个馒头,当前馒头的数量是:1
厨师1,我生产了一个馒头,当前的馒头数量是2
厨师2,我生产了一个馒头,当前的馒头数量是3
厨师1,我生产了一个馒头,当前的馒头数量是4
厨师2,我生产了一个馒头,当前的馒头数量是5
饭桶1,我吃了5个馒头,当前馒头的数量是:0
饭桶2想吃4只有0个了,快做馒头吧
饭桶3想吃3只有0个了,快做馒头吧
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶2,我吃了2个馒头,当前馒头的数量是:0
饭桶1想吃1只有0个了,快做馒头吧
饭桶3想吃2只有0个了,快做馒头吧
饭桶1想吃1只有0个了,快做馒头吧
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶3,我吃了1个馒头,当前馒头的数量是:1
饭桶2,我吃了1个馒头,当前馒头的数量是:0
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶3想吃4只有2个了,快做馒头吧
饭桶1,我吃了2个馒头,当前馒头的数量是:0
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶2想吃3只有2个了,快做馒头吧
饭桶3想吃3只有2个了,快做馒头吧
饭桶2,我吃了2个馒头,当前馒头的数量是:0
厨师2,我生产了一个馒头,当前的馒头数量是1
厨师1,我生产了一个馒头,当前的馒头数量是2
饭桶1想吃5只有2个了,快做馒头吧
饭桶3想吃5只有2个了,快做馒头吧
厨师2,我生产了一个馒头,当前的馒头数量是3
厨师1,我生产了一个馒头,当前的馒头数量是4
饭桶1想吃5只有4个了,快做馒头吧
饭桶3,我吃了3个馒头,当前馒头的数量是:1
饭桶2想吃5只有1个了,快做馒头吧
饭桶1想吃3只有1个了,快做馒头吧 Process finished with exit code -1

测试输出

公平锁和非公平锁:

锁Lock分为公平锁和非公平锁,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,也就是先进先出的顺序。而非公平锁就是一种获取锁的抢占机制,是随机获取的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁。如果我们用synchronized的方式我们没有办法使用公平锁。
创建公平锁的办法是在new ReentrantLock的时候加一个参数true.
new ReentrantLock(true)

java 多线程 Thread 锁ReentrantLock;Condition等待与通知;公平锁的更多相关文章

  1. java多线程20 : ReentrantLock中的方法 ,公平锁和非公平锁

    公平锁与非公平锁 ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得 ...

  2. “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  3. java并发编程——通过ReentrantLock,Condition实现银行存取款

         java.util.concurrent.locks包为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器.该框架允许更灵活地使用锁和条件,但以更难用的语法为代价. Lock 接口 ...

  4. java多线程6:ReentrantLock

    下面看下JUC包下的一大并发神器ReentrantLock,是一个可重入的互斥锁,具有比synchronized更为强大的功能. ReentrantLock基本用法 先来看一下ReentrantLoc ...

  5. Java多线程学习(四)等待/通知(wait/notify)机制

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79690279 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  6. Java多线程(五)——线程等待与唤醒

    一.wait().notify().notifyAll()等方法介绍 在Object.java中,定义了wait(), notify()和notifyAll()等接口.wait()的作用是让当前线程进 ...

  7. “全栈2019”Java多线程第二十九章:可重入锁与不可重入锁详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  8. “全栈2019”Java多线程第七章:等待线程死亡join()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  9. 【分布式锁】02-使用Redisson实现公平锁原理

    前言 前面分析了Redisson可重入锁的原理,主要是通过lua脚本加锁及设置过期时间来保证锁执行的原子性,然后每个线程获取锁会将获取锁的次数+1,释放锁会将当前锁次数-1,如果为0则表示释放锁成功. ...

随机推荐

  1. buu misc 1-32 wp

    buuCTFwp(1~32) 1.签到题 题里就有flag flag{buu_ctf} 2.二维码 1.题目是一个二维码,用010发现提示四位数字,想到应该是暗藏压缩包 2.虚拟机foremost分离 ...

  2. 9.2 k8s结合Jenkins与gitlab实现代码升级与回滚

    1.部署Jenkins 1.1 安装jdk # apt 安装jdk11 apt install openjdk-11-jdk # 查看 root@jenkins:~# java -version op ...

  3. ss 显示socket状态

    ss ===show socket用于显示socket状态 所有的TCP sockets 所有的UDP sockets 所有ssh/ftp/ttp/https持久连接 所有连接到Xserver的本地进 ...

  4. Linux—ps -ef 命令输出信息的具体含义(显示所有正在运行的命令程序)

    linux 中使用 ps -ef 输出参数的具体含义 功能:显示所有正在运行的命令程序 UID: 说明该程序被谁拥有PID:就是指该程序的 IDPPID: 就是指该程序父级程序的 IDC: 指的是 C ...

  5. mysql—mysql查询语句提示Unknown column ‘xxx’ in ‘where clause’

    运行结果中提示Unknown column 'xxx' in 'where clause'的问题.经过大神的指导,顿时明白其中缘由,如果sql中定义的类型是int型的可以不用加引号,但是如果是字符串类 ...

  6. Linux升级命令yum upgrade和yum update的区别

    Linux升级命令有两个分别是yum upgrade和yum update, 这个两个命令是有区别的: yum -y update 升级所有包同时也升级软件和系统内核 yum -y upgrade 只 ...

  7. rabbit mq的一个实例,异步功能

    简单的使用场景:消息队列的场景有:解耦,异步,削峰. 此例用的场景,异步 有时候会有请求消耗时间过长,不能老让用户等待返回结果,可以用消息队列来做异步实现,之前用过workmain等类似的异步,但不如 ...

  8. Redis | Redis常用命令及示例总结(API)

    目录 前言 1. Key(键) 1.1 键的基本操作功能 del move sort rename renamenx migrate 1.2 键的获取功能 type exists randomkey ...

  9. 虚拟机中安装centos系统的详细过程

    linux-centos的安装 检查电脑是否开启虚拟化,只有开启虚拟化才能安装虚拟机 新建虚拟机 鼠标点进去,选中红框所示,回车 登录: 输入默认用户名(超级管理员 root) 密码:安装时设置的密码

  10. nodeJs-querystring 模块

    JavaScript 标准参考教程(alpha) 草稿二:Node.js querystring 模块 GitHub TOP querystring 模块 来自<JavaScript 标准参考教 ...