Java锁的基本用法

2023-11-03

Java锁的基本用法

synchronized和lock

synchronized

synchronized是方法锁,可自动加锁和释放锁,因为其是方法级锁,也就是说将锁定整个方法的内容,所以对于业务逻辑较为复杂的方法,不建议用synchronized
需求:模拟三个人卖十张票,十张票代表不同的十个座位,正常情况下不能出现卖出两张一样的票

首先在没有加锁的情况下

在这里插入图片描述
测试结果
在这里插入图片描述

加锁的情况

直接在方法上加 synchronized
在这里插入图片描述
测试结果每张票都是唯一的,这里没有问题
在这里插入图片描述

Lock

需求:模拟三个人卖十张票,十张票代表不同的十个座位,正常情况下不能出现卖出两张一样的票

首先在没有加锁的情况下

package com.zhou.juc.sellingtickets;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author DELL
 * @version 1.0
 * @Description
 * @date 2022/5/17 17:16
 */
public class LockTicket implements Runnable{
    private int number=10;
    //可重入锁
    //final ReentrantLock rLock=new ReentrantLock();
    //加锁的情况下
    public void sellingTickets(){
        //上锁
        //rLock.lock();
        if(number>0) {
            System.out.println(Thread.currentThread().getName()+"  卖第"+(number--)+ "张票  当前剩于:" + number);
            try {
                //因为cpu处理效率较快,为了三个线程都能访问到这个方法,所以在此休息一下
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //解锁,这是必须的
        //rLock.unlock();
    }

    @Override
    public void run() {
        for (int i=0;i<10;i++)
            sellingTickets();
    }
}
class LockSellTickets{
    public static void main(String[]args){
        LockTicket ticket=new LockTicket();
        //同时开三个线程进行卖票
        new Thread(ticket,"A").start();
        new Thread(ticket,"B").start();
        new Thread(ticket,"C").start();
    }
}

测试结果出现同一张票,这是不正常的在这里插入图片描述

加锁的情况下

非公平锁和公平锁
非公平锁就是谁拿到锁谁就用,这里会造成一种情况就是有可能一个线程会把所有的任务给做了,其它线程没事可做,但是这种方式效率高
公平锁就是尽可能的给各线程分配相同量的任务,因为需要进行公平处理,所以会牺牲一定效率

//参数为true为公平锁
//final ReentrantLock rLock=new ReentrantLock(true);
//默认无参为非公平锁
final ReentrantLock rLock=new ReentrantLock();
public class LockTicket implements Runnable{
    private int number=10;
    //参数为true为公平锁
    //final ReentrantLock rLock=new ReentrantLock(true);
    //默认无参为非公平锁
    final ReentrantLock rLock=new ReentrantLock();
    //加锁的情况下
    public void sellingTickets(){
        //上锁
        rLock.lock();
        try {

            if (number > 0) {                System.out.println(Thread.currentThread().getName() + "  卖第" + (number--)+ "张票  当前剩于:" + number);
                try {
                    //因为cpu处理效率较快,为了三个线程都能访问到这个方法,所以在此休息一下
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        finally {
            //解锁,这是必须的
            rLock.unlock();
        }
    }

    @Override
    public void run() {
        for (int i=0;i<10;i++)
            sellingTickets();
    }
}
class LockSellTickets{
    public static void main(String[]args){
        LockTicket ticket=new LockTicket();
        //同时开三个线程进行卖票
        new Thread(ticket,"A").start();
        new Thread(ticket,"B").start();
        new Thread(ticket,"C").start();
    }
}

与没有加锁的区别在于这里加了可重入锁,为了避免出现异常而没有释放锁情况(死锁),需要把解锁放到finally中
在这里插入图片描述

测试结果是正常的情况
在这里插入图片描述

线程的通信

synchronized 通过wait和notifyAll进行通信

需求:一个生产者生产一个商品,两个消费者进行消费,并且生产者只能等消费者消费完之后才能生产,消费者只能等生产者生产完后才能消费

package com.zhou.juc.ThreadCommunication;

/**
 * @author DELL
 * @version 1.0
 * @Description
 * @date 2022/5/17 16:17
 */
public class SynTicket /*implements Runnable*/{

    private int number=0;
    //生产一个商品
    public synchronized void incr() throws InterruptedException {
        //判断商品是否被消费,如果没有就等待(这里一定要用while,用if会出现虚假唤醒的情况)
        while (number!=0) {
        //在这里进行等待,如果有多个线程调用incr,在此有可能被其它调用incr的线程进行唤醒
        //因为已经进入了判断,所以此时number有可能已经大于0,所以上面必须用while,而不能用if
            this.wait();
        }
        //生产一个商品
        System.out.println(Thread.currentThread().getName()+"生产一个商品前:"+number+"  生产一个商品后:"+ (++number));
        //通知其它线程可以干活了,这里是指所有线程都可以执行自己的任务了
        this.notifyAll();
    }
    //消费一个商品
    public synchronized void decr() throws InterruptedException {
        //判断商品是否被生产,如果没有就等待(这里一定要用while,用if会出现虚假唤醒的情况)
        while (number!=1) {
        //在这里进行等待,这里有B、C两个线程调用,如果B在此等待,有可能将被C唤醒
        //因为已经进入了判断,所以此时number有可能已经小于0,所以上面必须用while,而不能用if
            this.wait();
        }
        //消费一个商品
        System.out.println(Thread.currentThread().getName()+"消费一个商品前:"+number+"  消费一个商品后:"+ (--number));
        //通知其它线程可以干活了,这里是指所有线程都可以执行自己的任务了
        this.notifyAll();
    }

    //第一种方法
    /*@Override
    public void run() {
        for (int i=0;i<10;i++) {
            try {
                if("A".equals(Thread.currentThread().getName()))
                    incr();
                else
                    decr();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }*/
}

class SellTickets{
    public static void main(String[]args){
        SynTicket ticket=new SynTicket();
        //同时开三个线程进行,A为生产者,B、C为消费者

        //第一种方法
        /*new Thread(ticket,"A").start();
        new Thread(ticket,"B").start();
        new Thread(ticket,"C").start();*/

        //第二种方法
        incrOrDecr("A",ticket);
        incrOrDecr("B",ticket);
        incrOrDecr("C",ticket);

    }

    private static void incrOrDecr(String name, SynTicket ticket){
        new Thread(()->{
            for (int i=0;i<10;i++) {
                try {
                    if("A".equals(name))
                    ticket.incr();
                    else
                        ticket.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },name).start();
    }
}

测试结果A生产一个,B、C才能消费一个
在这里插入图片描述

synchronized 需求唤醒问题

把while改成if
在这里插入图片描述

测试结果不符合生产一个消费一个的需求
在这里插入图片描述

使用Lock定向唤醒线程

需求:
1、A生产五件商品,B先消费三件,C再消费两件
2、只有等C消费完后,A再生产,整个流程循环五次

代码中用到了Condition接口,Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现使线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。

package com.zhou.juc.ThreadCommunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author DELL
 * @version 1.0
 * @Description
 * @date 2022/5/18 10:41
 */
public class LockDirectonalAwaken {
    //标志量1,默认A先执行
    int flag=1;
    //商品数量
    int number=0;
    //定义一把锁
    ReentrantLock rLock=new ReentrantLock();

    Condition conditionA=rLock.newCondition();
    Condition conditionB=rLock.newCondition();
    Condition conditionC=rLock.newCondition();


    public void doA() throws InterruptedException {
        rLock.lock();

        try{
            while(flag!=1){
                //A进行等待
                conditionA.await();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+"生产一个商品前:"+number+"  生产一个商品后:"+ (++number));
            }
            //标志量改成B的
            flag=2;
            //定向唤醒B
            conditionB.signal();

        }finally{
            rLock.unlock();
        }
    }

    public void doB() throws InterruptedException {
        rLock.lock();
        try{
            while(flag!=2){
                //B进行等待
                conditionB.await();
            }
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName()+"消费一个商品前:"+number+"  消费一个商品后:"+ (--number));
            }
            //标志量改成C的
            flag=3;
            //定向唤醒C
            conditionC.signal();

        }finally{
            rLock.unlock();
        }
    }

    public void doC() throws InterruptedException {
        rLock.lock();
        try{
            while(flag!=3){
                //C进行等待
                conditionC.await();
            }
            for (int i = 0; i < 2; i++) {
                System.out.println(Thread.currentThread().getName()+"消费一个商品前:"+number+"  消费一个商品后:"+ (--number));
            }
            //标志量改成A的
            flag=1;
            //定向唤醒A
            conditionA.signal();
        }finally{
            rLock.unlock();
        }
    }
}

class DirectonalAwaken{
    public static void main(String[]args){
        LockDirectonalAwaken ldAwaken=new LockDirectonalAwaken();
        incrOrDecr("A",ldAwaken);
        incrOrDecr("B",ldAwaken);
        incrOrDecr("C",ldAwaken);
    }
    private static void incrOrDecr(String name, LockDirectonalAwaken ldAwaken){
        new Thread(()->{
            for (int i = 0; i < 5; i++) {

                try {
                    switch (name){
                        case "A":
                            ldAwaken.doA();
                            break;
                        case "B":
                            ldAwaken.doB();
                            break;
                        case "C":
                            ldAwaken.doC();
                            break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },name).start();
    }
}

测试结果 A+5 B-3 C-2 A+5 B-3 C-2 符合顺序执行的业务逻辑
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java锁的基本用法 的相关文章

  • 使用 Maven 生成 Eclipse 项目文件

    当我尝试使用生成 Eclipse 项目文件时mvn eclipse eclipse我收到以下错误 插件管理器执行目标时出现内部错误 org apache maven plugins maven eclipse plugin 2 9 SNAP
  • JNA - EnumProcessModules() 未返回所有 DLL?

    我试图从游戏中读取坐标 当我在通过 OpenProcess 接收的 HANDLE 上使用 ReadProcessMemory 以及我在 CheatEngine 中找到的内存时 效果非常好 例如 如果我知道正在运行的进程中的浮点值是0x5AB
  • Java 字符串哈希码缓存

    字符串不变性的优点之一是哈希码缓存以实现更快的访问 在这种情况下 如何处理具有相同哈希码的字符串的缓存 在这种情况下它真的能提高性能吗 在这种情况下 如何处理具有相同哈希码的字符串的缓存 被缓存的是字符串的哈希码 它被缓存在私有的int字符
  • Java中RandomAccessFile的并发

    我正在创建一个RandomAccessFile对象通过多个线程写入文件 在 SSD 上 每个线程都尝试在文件中的特定位置写入直接字节缓冲区 并且我确保线程写入的位置不会与另一个线程重叠 file getChannel write buffe
  • 通过 html tidy 提供渲染 jsp 页面

    我有一个在 Glassfish 上运行的 Java 项目 它会呈现一些难看的 HTML 这是使用各种内部和外部 JSP 库的副作用 我想设置某种渲染后过滤器 通过 HTMLTidy 提供最终的 HTML 这样源代码就很好且整洁 有助于调试
  • Java中的文字赋值[重复]

    这个问题在这里已经有答案了 定义上有什么区别 double example 23 1d or double example 23 1 为什么long float double可以以l f d结尾 之间没有区别double example 2
  • Spring boot 2.0.5.RELEASE和mongo 4.0连接问题

    我正在关注使用 MongoDB 访问数据教程春季网站 https spring io guides gs accessing data mongodb 我将 Mongo DB 服务器版本 4 安装为服务当我使用客户端连接到它时 它的身份验证
  • AffineTransform.rotate() - 如何同时缩放、旋转和缩放?

    我有以下代码 它可以完成我想要绘制一个上面有一些棋子的棋盘的 第一部分 Image pieceImage getImage currentPiece int pieceHeight pieceImage getHeight null dou
  • Android 游戏偶尔出现延迟

    我正在用 Java 制作一个简单的 Android 游戏 我注意到每 20 40 秒就会出现一些烦人的延迟 首先 我认为它们是由垃圾收集器引起的 但当我检查 LogCat 时 我发现游戏滞后时没有垃圾收集 每当游戏开始滞后时 我都会标记日志
  • 如何在 Java 中使用 StringUtils?

    我是 Java 初学者 我想用StringUtils replace但 Eclipse 输出 StringUtils cannot be resolved I tried import java lang 但它不起作用 java lang不
  • net.sf.jasperreports.engine.JRRuntimeException:java.io.IOException:无法读取字体数据

    我正在尝试通过 JasperReport 创建 PDF 报告 但读取字体数据时出现问题 我有 jasperreports extension properties 和 ClassPath 中的相关 TTF 文件 这是错误 java io I
  • 两条腿的 OAuth 和 Gmail Atom feed

    我们正在尝试让 2 legged OAuth 与 Gmail Atom feed 一起使用 我们使用 John Kristian Praveen Alavilli 和 Dirk Ba lfanz 贡献的 Java 库 http oauth
  • Java字符串查找和替换的最佳方法?

    我正在寻找 Java 中字符串查找和替换的最佳方法 这是一句话 我的名字叫米兰 人们都知道我叫米兰瓦西奇 我想用 Milan Vasic 替换 Milan 弦 但在我已经有 Milan Vasic 的地方 情况不应该是这样 搜索 替换后的结
  • Java LRU 缓存使用 LinkedList

    堆栈溢出的新手 所以请不要介意我以菜鸟的方式问这个问题 我正在尝试使用链表实现 LRU 缓存 我在这里看到了使用 linkedHashMap 和其他数据结构的其他实现 但对于这种情况 我正在尝试使用链表创建最佳优化版本 正如我在技术期间被问
  • javax.xml.bind.JAXBException: 类 *** 及其任何超类在此上下文中均未知

    我正在尝试通过 REST Web 服务传递对象 以下是我的课程 使用一些示例代码解释了我需要的功能 Rest Web 服务类方法 POST Path find Consumes MediaType APPLICATION FORM URLE
  • Java 中更高级的泛型

    假设我有以下课程 public class FixExpr Expr
  • 在 REST Web 服务中接受逗号分隔值

    我正在尝试接收 REST URI 中以逗号分隔值形式的字符串列表 示例 http localhost 8080 com vogella jersey first rest todo test 1 abc test 其中 abc 和 test
  • 如何列出Resources文件夹中的所有文件(java/scala)

    我正在编写一个函数 需要访问资源中的文件夹 并循环遍历所有文件名 如果这些文件符合条件 则加载这些文件 new File getClass getResource images sprites getPath listFiles 返回空指针
  • 使用正则表达式匹配阿拉伯文文本

    我试图使用正则表达式仅匹配阿拉伯语文本 但出现异常 这是我的代码 txt matches P Arabic 这是例外情况 线程 main 中的异常 java util regex PatternSyntaxException 索引 9 附近
  • 如何创建具有同等时间元素的 JavaFX 转换?

    我正在尝试 JavaFX 和动画 尤其是PathTransition 我正在创建一个简单的程序 使球 弹跳 而不使用QuadCurveTo班级 到目前为止 这是我的代码 Ellipse ball new Ellipse 375 250 10

随机推荐

  • SQL根据不同状态类型动态使用不同排序方式

    SQL根据不同状态类型使用不同排序方式 1 业务场景 1 1 需求 默认按状态值为1 2 3 5 4的顺序排序 状态相同时 状态为1 则按创建时间倒序 状态为2 则按开始时间正序 1 2 示例SQL select from t table
  • 【FPGA面试题(八)】—— 实现简单的FIFO

    FPGA面试题 八 实现简单的FIFO 在FPGA的数字电路中 FIFO First In First Out 是一个非常基础且重要的模块 它可以像一个队列一样存储数据并按照先进先出的顺序进行读取 实现FIFO是FPGA工程师面试过程中经常
  • notepad: 怎么在notepad里面,将字符串替换成换行

    用Notepad 可以 利用查找和替换功能 选择正则表达式 查找目标框里输入你想要替换的字符串 替换为框里输入 r 点击替换即可 这是个 很强大的功能 利用还它可以批量替换任何字符串 比如你链接是123 123 123 123这样不换行的
  • python 实现 熵值法 确定指标权重

    步骤 设指标共p个 评价对象共g个 则构成评价值 得分 矩阵如下 xj i 表示评价对象j在指标i上的评价值 j 1 2 g i 1 2 p 指标i对应的熵值为ei的计算公式如下 根据熵值来计算指标i的权重wi 计算公式如下 程序 4个指标
  • pm2 进程管理工具,相关命令

    1 pm2需要全局安装 npm install g pm2 2 进入项目根目录 2 1 启动进程 应用 pm2 start bin www 或 pm2 start app js 2 2 重命名进程 应用 pm2 start app js n
  • minicom安装、配置和使用

    在开发过程中 我们经常需要通过串口连接Android开发板的底层系统 Linux QNX 等 minicom是一个常用串口连接终端软件 在命令行终端下通过文本界面进行操作使用 安装 sudo apt install minicom 配置 首
  • BufferedReader与FileReader及FileInputStream

    BufferedReader 是缓冲字符输入流 它继承于Reader BufferedReader 的作用是为其他字符输入流添加一些缓冲功能 BufferedReader的作用 从字符输入流中读取文本 缓冲各个字符 从而实现字符 数组和行的
  • C# Ocr离线式识别,文字提取,(附源码下载)

    源代码下载 效果图 文字内容提取后填充 JObject obj ocr GeneralBasic bt ops this richTextBox1 Text string str if obj Count gt 0 JArray jo JA
  • LeetCode - 移除元素

    一 题目描述 给定一个数组 nums 和一个值 val 你需要原地移除所有数值等于 val 的元素 返回移除后数组的新长度 不要使用额外的数组空间 你必须在原地修改输入数组并在使用 O 1 额外空间的条件下完成 元素的顺序可以改变 你不需要
  • GBase8a MPP Cluster 安装部署——操作系统配置建议

    编写目的 本文档面向GBase 8a产品的售后人员 用户使用人员 以及广大GBase 8a感兴趣的技术人员 以便用于指导其更好的完成GBase8a MPP Cluster 安装部署工作 对硬件配置 网络环境 操作系统及软件配置等系统实施过程
  • vue 接口数据返回之后再渲染页面_Vue怎么让数据请求成功以后再渲染页面?

    需求如下 进入页面有一个检测按钮 点击即可向后端请求数据 进入页面如果不点击检测 则显示如下 点击检测 如果返回的是正常的状态则显示 如果返回的状态是异常 则显示 目前有个BUG 就是点击检测的时候 先从变为 然后马上变为 因为逻辑里面我是
  • IJCAI2023 Summary Reject公布

    点击文末公众号卡片 找对地方 轻松参会 北京时间2023年2月25日上午6点四十左右 cmt上状态已变 分为awaiting list 和reject 此前不少人预测2月24日晚上八点或凌晨两点左右出 截至2023年2月25日 7 16 a
  • 调用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()时遇到的一些问题

    之前在其他地方写的 一直要求手机验证 之前能跳过 麻烦点就麻烦点了 今天编辑文章的时候直接不能改了 无奈 如果手动调用 TransactionAspectSupport currentTransactionStatus setRollbac
  • mybatis获取插入数据时自动生成的主键id

    mapper文件 void insert Map
  • LaTeX

    简介 首先要介绍一下我用的Visio文件转为 eps的办法 vsd 文件 利用Visio打开 然后另存为 选择存为 pdf文件 pdf文件 利用Inkscape打开 然后另存为 选择存为 eps格式 之前一直用visio2010版 然后按照
  • 【React+TS】从零开始搭建react+typescript+router+redux+less+px2rem自适应+axios反向代理+别名@+Antd-mobile

    一 通过create react app脚手架创建项目 npx create react app testproject template typescript 在vscode中打开项目 可以看到顺利生成了react项目且组件的后缀为tsx
  • Java web项目创建笔记23 之《spring整合xxl-job》

    xxl job是一款功能强大的分布式任务调度系统 部署方法按照官网写的说明即可 https www xuxueli com xxl job 1 下载release版本代码 https github com xuxueli xxl job r
  • 先电Openstack云平台搭建【超级详细】【附带镜像】

    前言 大二上学期学习Openstack 苦于百度与CSDN上没有对应版本的教程 学的十分艰难 在此 将我的Openstack云平台搭建过程写出 留给新手学习 准备工作 VMware Workstation Pro 虚拟机 我使用版本 15
  • C++模板,模板具体化,特例化

    1 模板重载原则 函数同名 重载 时 调用优先级通常为 普通函数 gt 显式具体化 template specilazation gt 显式实例化 gt 一般模版函数 但更一般而言 有两条规则 1 gt 如果各自函数形参和调用处的实参 并非
  • Java锁的基本用法

    文章目录 Java锁的基本用法 synchronized和lock synchronized 首先在没有加锁的情况下 加锁的情况 Lock 首先在没有加锁的情况下 加锁的情况下 线程的通信 synchronized 通过wait和notif