(八) 共享模型之管程【ReentrantLock】

news2024/9/22 17:26:10

相对于 synchronized 具备如下特定:

(1)可中断

(2)可以设置超市时间

(3)可以设置为公平锁

(4)支持多个条件变量

与 synchronized 一样,都支持可重入

 基本语法

一、可重入(P121)

可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁。

如果是不可重入,那么第二次获得锁时,自己也会被锁挡住。

@Slf4j(topic = "c.Test22")
public class Test22 {

    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        lock.lock();
        try {
            log.debug("enter main");
            m1();
        } finally {
            lock.unlock();
        }
    }

    public static void m1(){
        lock.lock();
        try {
            log.debug("enter m1");
            m2();
        } finally {
            lock.unlock();
        }
    }

    public static void m2(){
        lock.lock();
        try {
            log.debug("enter m2");
        } finally {
            lock.unlock();
        }
    }
}

二、可打断

@Slf4j(topic = "c.Test22")
public class Test22 {

    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            try {
                //如果没有竞争那么此方法就会获取lock对象锁
                //如果有竞争就进入阻塞队列,可以被其它线程用interruput方法打断
                log.debug("尝试获取锁");
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("没有获得锁,返回");
                return;
            }
            try {
                log.debug("获取到锁");
            } finally {
                lock.unlock();
            }
        },"t1");

        lock.lock();
        t1.start();

        Thread.sleep(1000);
        log.debug("打断 t1");
        t1.interrupt();
    }
}
注意如果是 不可中断(lock()方法) 模式,那么即使使用了 interrupt 也不会让等待中断

三、锁超时

@Slf4j(topic = "c.Test22")
public class Test22 {

    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            log.debug("尝试获取锁");
            // 立刻尝试获取锁
            boolean tryLock = lock.tryLock();
            if (!tryLock){
                log.debug("获取不到锁");
                return;
            }
            try {
                log.debug("获得到锁");
            } finally {
                lock.unlock();
            }
        },"t1");

        log.debug("获取锁");
        lock.lock();
        t1.start();
    }
}

 

@Slf4j(topic = "c.Test22")
public class Test22 {

    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
            log.debug("尝试获取锁");
            try {
                // 2s后尝试获取锁
                boolean tryLock = lock.tryLock(2, TimeUnit.SECONDS);
                if (!tryLock){
                    log.debug("获取不到锁");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.debug("获取不到锁");
                return;
            }
            try {
                log.debug("获得到锁");
            } finally {
                lock.unlock();
            }
        },"t1");

        log.debug("获取锁");
        lock.lock();
        t1.start();
    }
}

使用 tryLock 解决哲学家就餐问题
@Slf4j(topic = "c.Test23")
public class Test23 {public static void main(String[] args) {
    Chopstick c1 = new Chopstick("1");
    Chopstick c2 = new Chopstick("2");
    Chopstick c3 = new Chopstick("3");
    Chopstick c4 = new Chopstick("4");
    Chopstick c5 = new Chopstick("5");
    new Philosopher("苏格拉底", c1, c2).start();
    new Philosopher("柏拉图", c2, c3).start();
    new Philosopher("亚里士多德", c3, c4).start();
    new Philosopher("赫拉克利特", c4, c5).start();
    new Philosopher("阿基米德", c5, c1).start();
}
}

@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread {
    Chopstick left;
    Chopstick right;

    public Philosopher(String name, Chopstick left, Chopstick right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while (true) {
            // 尝试获得左手筷子
            if(left.tryLock()) {
                try {
                    // 尝试获得右手筷子
                    if(right.tryLock()) {
                        try {
                            eat();
                        } finally {
                            right.unlock();
                        }
                    }
                } finally {
                    left.unlock(); // 释放自己手里的筷子
                }
            }
        }
    }

    Random random = new Random();
    private void eat() {
        log.debug("eating...");
        Sleeper.sleep(0.5);
    }
}

class Chopstick extends ReentrantLock {
    String name;

    public Chopstick(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "筷子{" + name + '}';
    }
}

四、公平锁

ReentrantLock 默认是不公平的

// true:公平
// false(默认):不公平
ReentrantLock lock = new ReentrantLock(true);

公平锁一般没有必要,会降低并发度,后面分析原理时会讲解。

五、条件变量

synchronized 中也有条件变量,就是我们讲原理的 waitSet 休息室,当条件不满足时进入等待。

ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持多个条件变量的,这就好比:

(1)synchronized 是那些不满足条件的线程都在一间休息室等消息

(2)而 ReentrantLock 支持多间休息室,唤醒时也是按休息室来唤醒的

使用要点:

(1)await 前需要获得锁

(2)await 执行后,会释放锁,进入 conditionObject 等待

(3)await 的线程被唤醒(或打断、或超时)去重新竞争 lock 锁

(4)竞争 lock 锁成功后,从 await 后继续执行

@Slf4j(topic = "c.Test22")
public class Test22 {

    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        // 创建一个新的条件变量(休息室)
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();

        lock.lock();
        // 进入休息室等待
        condition1.await();

        condition1.signal();
        condition1.signalAll();

    }
}
@Slf4j(topic = "c.Test24")
public class Test24 {
    static final Object room = new Object();
    static boolean hasCigarette = false;
    static boolean hasTakeout = false;
    static ReentrantLock ROOM = new ReentrantLock();
    // 等待烟的休息室
    static Condition waitCigaretteSet = ROOM.newCondition();
    // 等外卖的休息室
    static Condition waitTakeoutSet = ROOM.newCondition();

    public static void main(String[] args) {


        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("有烟没?[{}]", hasCigarette);
                while (!hasCigarette) {
                    log.debug("没烟,先歇会!");
                    try {
                        waitCigaretteSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小南").start();

        new Thread(() -> {
            ROOM.lock();
            try {
                log.debug("外卖送到没?[{}]", hasTakeout);
                while (!hasTakeout) {
                    log.debug("没外卖,先歇会!");
                    try {
                        waitTakeoutSet.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("可以开始干活了");
            } finally {
                ROOM.unlock();
            }
        }, "小女").start();

        sleep(1);
        new Thread(() -> {
            ROOM.lock();
            try {
                hasTakeout = true;
                waitTakeoutSet.signal();
            } finally {
                ROOM.unlock();
            }
        }, "送外卖的").start();

        sleep(1);

        new Thread(() -> {
            ROOM.lock();
            try {
                hasCigarette = true;
                waitCigaretteSet.signal();
            } finally {
                ROOM.unlock();
            }
        }, "送烟的").start();
    }

}

六、同步模式之顺序控制

1. 固定运行顺序

1.1 wait notify

@Slf4j(topic = "c.Test25")
public class Test25 {
    static final Object lock = new Object();
    // 表示 t2 是否运行过
    static boolean t2runned = false;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                while (!t2runned) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                log.debug("1");
            }
        }, "t1");


        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                log.debug("2");
                t2runned = true;
                lock.notify();
            }
        }, "t2");

        t1.start();
        t2.start();
    }
}

1.2 Park Unpark

@Slf4j(topic = "c.Test26")
public class Test26 {
    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            LockSupport.park();
            log.debug("1");
        }, "t1");
        t1.start();

        new Thread(() -> {
            log.debug("2");
            LockSupport.unpark(t1);
        },"t2").start();
    }
}
park unpark 方法比较灵活,他俩谁先调用,谁后调用无所谓。并且是以线程为单位进行『暂停』和『恢复』, 不需要『同步对象』和『运行标记』

2. 交替输出

线程 1 输出 a 5 次,线程 2 输出 b 5 次,线程 3 输出 c 5 次。现在要求输出  abcabcabcabcabc 怎么实现

2.1 wait notify

@Slf4j(topic = "c.Test27")
public class Test27 {
    public static void main(String[] args) {
        WaitNotify wn = new WaitNotify(1, 5);
        new Thread(() -> {
            wn.print("a", 1, 2);
        }).start();
        new Thread(() -> {
            wn.print("b", 2, 3);
        }).start();
        new Thread(() -> {
            wn.print("c", 3, 1);
        }).start();
    }
}

/*
输出内容       等待标记     下一个标记
   a           1             2
   b           2             3
   c           3             1
 */
class WaitNotify {
    // 打印               a           1             2
    public void print(String str, int waitFlag, int nextFlag) {
        for (int i = 0; i < loopNumber; i++) {
            synchronized (this) {
                while(flag != waitFlag) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.print(str);
                flag = nextFlag;
                this.notifyAll();
            }
        }
    }

    // 等待标记
    private int flag; // 2
    // 循环次数
    private int loopNumber;

    public WaitNotify(int flag, int loopNumber) {
        this.flag = flag;
        this.loopNumber = loopNumber;
    }
}

2.2 Lock 条件变量版

public class Test30 {
    public static void main(String[] args) throws InterruptedException {
        AwaitSignal awaitSignal = new AwaitSignal(5);
        Condition a = awaitSignal.newCondition();
        Condition b = awaitSignal.newCondition();
        Condition c = awaitSignal.newCondition();
        new Thread(() -> {
            awaitSignal.print("a", a, b);
        }).start();
        new Thread(() -> {
            awaitSignal.print("b", b, c);
        }).start();
        new Thread(() -> {
            awaitSignal.print("c", c, a);
        }).start();

        Thread.sleep(1000);
        awaitSignal.lock();
        try {
            System.out.println("开始...");
            a.signal();
        } finally {
            awaitSignal.unlock();
        }

    }
}

class AwaitSignal extends ReentrantLock{
    private int loopNumber;

    public AwaitSignal(int loopNumber) {
        this.loopNumber = loopNumber;
    }
    // 参数1 打印内容, 参数2 进入哪一间休息室, 参数3 下一间休息室
    public void print(String str, Condition current, Condition next) {
        for (int i = 0; i < loopNumber; i++) {
            lock();
            try {
                current.await();
                System.out.print(str);
                next.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                unlock();
            }
        }
    }
}

2.3 Park Unpark

@Slf4j(topic = "c.Test31")
public class Test31 {

    static Thread t1;
    static Thread t2;
    static Thread t3;
    public static void main(String[] args) {
        ParkUnpark pu = new ParkUnpark(5);
        t1 = new Thread(() -> {
            pu.print("a", t2);
        });
        t2 = new Thread(() -> {
            pu.print("b", t3);
        });
        t3 = new Thread(() -> {
            pu.print("c", t1);
        });
        t1.start();
        t2.start();
        t3.start();

        LockSupport.unpark(t1);
    }
}

class ParkUnpark {
    public void print(String str, Thread next) {
        for (int i = 0; i < loopNumber; i++) {
            LockSupport.park();
            System.out.print(str);
            LockSupport.unpark(next);
        }
    }

    private int loopNumber;

    public ParkUnpark(int loopNumber) {
        this.loopNumber = loopNumber;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/57728.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

回归预测 | MATLAB实现基于RF随机森林的用水量预测(多因素、多指标)

回归预测 | MATLAB实现基于RF随机森林的用水量预测(多因素、多指标) 目录 回归预测 | MATLAB实现基于RF随机森林的用水量预测(多因素、多指标)预测效果基本介绍模型原理程序设计参考资料预测效果 基本介绍 将随机森林回归原理应用到了预测领域,构建了基于随机森林的预测模型,…

Spring-Cloud-Zipkin-05

前言 1、链路追踪由来&#xff1a;在微服务框架中&#xff0c;一个由客户端发起的请求在后端系统中会经过多个不同的服务节点调用来协同产生最后的请求结果&#xff0c;每一个请求都会开成一条复杂的分布式服务调用链路&#xff0c;链路中的任何一环出现高延时或错误都会引导起…

一文读懂vue3和vue2的API风格对比

Vue3 组合式 API&#xff08;Composition API&#xff09; 主要用于在大型组件中提高代码逻辑的可复用性。 传统的组件随着业务复杂度越来越高&#xff0c;代码量会不断的加大&#xff0c;整个代码逻辑都不易阅读和理解。 Vue3 使用组合式 API 的地方为 setup。 在 setup 中…

JavaEE-多线程初阶1

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录1.认识线程1.1概念1.2多线程程序1.3创建线程2.Thread类及常见方法2.1Thread 的常见构造方法2.2 Thread 的几个常见属性2.3中断…

在本地利用服务器显卡跑代码

除了使用xshell等连接服务器以外&#xff0c;pycharm也可以连接服务器&#xff0c;在服务器上运行代码&#xff0c;上传下载文件等操作。 参考&#xff1a;https://cloud.tencent.com/developer/article/1738482 步骤如下&#xff1a; 1、pycharm工具栏&#xff1a;Tools– D…

基于51单片机的压力监测仪(MPX4115)(Proteus仿真+程序)

编号&#xff1a;28 基于51单片机的压力监测仪(MPX4115) 功能描述&#xff1a; 本设计由51单片机最小系统MPX4115压力传感器ADC0832模块液晶1602模块 1、主控制器是AT89C82单片机 2、MPX4115压力传感器采集气压力&#xff0c;通过ADC0832模数转换器进行A/D转换&#xff0c;读…

Java语言与系统设计课程实验报告

做个课设做的我人间失格&#xff0c;写了一晚上没保存&#xff0c;真是哭死 一、目的与要求 &#xff08;一&#xff09;、实验目的 掌握Java语言与系统设计的基本思路和方法。 利用所学的基本知识和技能&#xff0c;解决简单的Java语言与系统设计问题。 &#xff08;二&…

挂耳式蓝牙耳机性价比推荐,几款高性能的耳机分享

无论是在日常还是运动的场景下&#xff0c;我们通常都会选择佩戴着耳机&#xff0c;让我们能够顺利过渡掉枯燥的生活&#xff0c;之前人们会选择入耳式的耳机&#xff0c;在长期佩戴过后会有不小的疾病诞生&#xff0c;在近些年迅速火起的骨传导耳机成为了焦点&#xff0c;其保…

Java线程池理解与学习

线程过多就容易引发内存溢出&#xff0c;因此我们有必要使用线程池的技术 线程池的好处 降低资源消耗&#xff1a; 通过重复利用已创建的线程降低线程创建和销毁造成的消耗 提高响应速度&#xff1a; 当任务到达时&#xff0c;任务可以不需要等待线程创建就能立即执行 提高线…

GEE:关系、条件和布尔运算

ee.Image对象具有一组用于构建决策表达式的关系、条件和布尔运算方法。这些方法可以用来掩膜、绘制分类地图和重新赋值。 本文记录了在GEE&#xff08;Google Earth Engine&#xff09;平台上的关系运算符和布尔运算符&#xff0c;分别应用到了三个不用的场景&#xff08;筛选低…

【坚持不懈的每日一题——力扣篇】1796. 字符串中第二大的数字(简单)+set 用法复习

GitHub同步更新&#xff08;已分类&#xff09;&#xff1a;Data_Structure_And_Algorithm-Review 公众号&#xff1a;URLeisure 的复习仓库 公众号二维码见文末 以下是本篇文章正文内容&#xff0c;下面案例可供参考。 一、题目描述 力扣今天推的每日一题是道简单题&#x…

[附源码]计算机毕业设计心理健康系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

读写锁三种关系的证明(读者和读者互补影响、写者和写者互斥、读者和写者互斥)

目录 1、读者和读者互不影响 2、写者和写者互斥 3、读者和写者互斥 (1) 读者持有锁 (2) 写者持有锁 1、读者和读者互不影响 假设现在只有读者线程&#xff0c;我们让一个读者线程申请锁以后&#xff0c;但是不释放读写锁。 #include <stdio.h> #include <unist…

[附源码]JAVA毕业设计计算机专业在线学习评估软件-演示录像-(系统+LW)

[附源码]JAVA毕业设计计算机专业在线学习评估软件-演示录像-&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&…

HTML小游戏15 —— 网页版3D反恐英雄(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且实用的计…

[附源码]Python计算机毕业设计Django教学辅助系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

2022年各大企业java面试题解析,堪称全网最详细的java面试指南

前言 最近感慨面试难的人越来越多了&#xff0c;一方面是市场环境&#xff0c;更重要的一方面是企业对Java的人才要求越来越高了。 ​基本上这样感慨的分为两类人&#xff0c;第一&#xff0c;虽然挂着3、5年经验&#xff0c;但肚子里货少&#xff0c;也没啥拿得出手的项目&am…

膜拜,终于拿到了美团大佬分享的Netty源码剖析与应用PDF

前言 时间飞逝&#xff0c;转眼间毕业七年多&#xff0c;从事 Java 开发也六年了。我在想&#xff0c;也是时候将自己的 Java 整理成一套体系。 这一次的知识体系面试题涉及到 Java 知识部分、性能优化、微服务、并发编程、开源框架、分布式等多个方面的知识点。 写这一套 Ja…

机器学习笔记之受限玻尔兹曼机(二)模型表示

机器学习笔记之受限玻尔兹曼机——模型表示引言回顾&#xff1a;玻尔兹曼分布玻尔兹曼机关于玻尔兹曼机的问题受限玻尔兹曼机受限玻尔兹曼机的学习任务(填坑)引言 上一节基于马尔可夫随机场介绍了玻尔兹曼分布&#xff0c;本节将介绍受限玻尔兹曼机的模型表示(Representation)…

阿里资深专家分享程序员三门课:技术精进架构修炼、管理探秘文档

前言 学习是一种基础性的能力。然而&#xff0c;“吾生也有涯&#xff0c;而知也无涯。”&#xff0c;如果学习不注意方法&#xff0c;则会“以有涯随无涯&#xff0c;殆矣”。 学习就像吃饭睡觉一样&#xff0c;是人的一种本能&#xff0c;人人都有学习的能力。我们在刚出生…