java多线程同步技术基础

news2024/11/24 7:58:49

说明

当程序中出现多个进程对同一资源进行操作时,因为对数据的操作非常密集,可能会对资源过度操作,这时就需要用到线程的同步技术。

以一个抢红包程序为例,红包数量为3个,开启5个线程来模拟抢红包行为,红包数量抢完了,就不能再抢了。代码如下:

/**
 * 抢红包
 */
class GetBonus implements Runnable {

    /**
     * 创建一个红包数量
     */
    private static int bonusQuantity = 3;

    @Override
    public void run() {
        // 如果红包数量大于0
        if (bonusQuantity > 0) {
            System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");
            // 红包数量-1
            bonusQuantity--;
        } else {
            System.out.println(Thread.currentThread().getName() + "抱歉,红包已经被抢完了");
        }
        System.out.println(bonusQuantity);
    }
}
public class EssayTest {
    private static void main(String[] args) {
        // 创建红包线程
        GetBonus getBonus = new GetBonus();

        // 创建5个Thread线程并命名、启动
         new Thread(getBonus, "黄蓉").start();
         new Thread(getBonus, "郭靖").start();
         new Thread(getBonus, "杨过").start();
         new Thread(getBonus, "小龙女").start();
         new Thread(getBonus, "张无忌").start();
    }
}

在这里插入图片描述

线程同步方式

为了解决上面对线程对统一资源操作不同步的问题,有以下几种方式

一、syschronized代码块

syschronized是java的关键字,被syschronized代码块包含的程序,同时只能有一个线程在执行。可以将程序中对资源操作的核心代码包起来,实现线程同步。修改后的代码如下:

/**
 * 抢红包
 */
class GetBonus implements Runnable {

    /**
     * 创建一个红包数量
     */
    private static int bonusQuantity = 3;

    @Override
    public void run() {
        // 用synchronized代码块将核心代码包含起来,()内的必须是一个对象且唯一,通常用this,即GetBonus类的地址,在内存中是唯一的。
        synchronized (this) {
            // 如果红包数量大于0
            if (bonusQuantity > 0) {
                System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");
                // 红包数量-1
                bonusQuantity--;
            } else {
                System.out.println(Thread.currentThread().getName() + "抱歉,红包已经被抢完了");
            }
            System.out.println("红包剩余:" + bonusQuantity);
            System.out.println("---------------------------------");
        }
    }
}

在这里插入图片描述

二、锁方法

即将方法用syschronized修饰,表明该方法是一个同步方法,同时只能有一个线程在执行,修改后的代码如下:

/**
 * 抢红包
 */
class GetBonus implements Runnable {

    /**
     * 创建一个红包数量
     */
    private static int bonusQuantity = 3;

    @Override
    public void run() {
        get();
    }

    /**
     * 将抢红包的代码抽取成一个方法,并用synchronized修饰
     */
    private synchronized void get() {
        // 如果红包数量大于0
        if (bonusQuantity > 0) {
            System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");
            // 红包数量-1
            bonusQuantity--;
        } else {
            System.out.println(Thread.currentThread().getName() + "抱歉,红包已经被抢完了");
        }
        System.out.println("红包剩余:" + bonusQuantity);
        System.out.println("---------------------------------");
    }
}

在这里插入图片描述
值得思考的是,这里可不可以将run()方法用synchronized修饰,设置成一个同步方法,一步到位呢?

三、ReentrantLock

使用java的ReentrantLock类,自定义设置上锁和解锁的代码区间,修改后的代码如下:

import java.util.concurrent.locks.ReentrantLock;

/**
 * 抢红包
 */
class GetBonus implements Runnable {

    /**
     * 创建一个红包数量
     */
    private static int bonusQuantity = 3;

    /**
     * 创建一个锁对象
     */
    ReentrantLock lock = new ReentrantLock();
    
    @Override
    public void run() {
        // 加锁
        lock.lock();
        // 如果红包数量大于0
        if (bonusQuantity > 0) {
            System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");
            // 红包数量-1
            bonusQuantity--;
        } else {
            System.out.println(Thread.currentThread().getName() + "抱歉,红包已经被抢完了");
        }
        // 解锁
        lock.unlock();
        System.out.println("红包剩余:" + bonusQuantity);
        System.out.println("---------------------------------");
    }
}

在这里插入图片描述
值得一提的是,阿里巴巴代码规范提示,lock.lock()必须紧跟try代码块,且unlock要放到finally第一行。应该是考虑到代码被上锁后,解锁的lock.unlock()代码有可能一直没被执行的情况。

总结

如果涉及到多线程对同一资源的操作,为了避免出现资源溢出的情况,可以用synchronized代码块、synchronized方法和ReentrantLock类对象上锁、解锁等方法,在实际使用时,还应该考虑到,被锁住的代码越多,程序执行效率越低,所以应该尽量只锁住“核心代码”。

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

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

相关文章

[MAUI]在.NET MAUI中复刻苹果Cover Flow

文章目录 原理3D旋转平行变换 创建3D变换控件绘制封面图片应用3D旋转应用平行变换绘制倒影创建绑定属性 创建绑定数据创建布局计算位置计算3D旋转 创建动效项目地址 Cover Flow是iTunes和Finder中的一个视图选项,允许用户使用水平滚动的图像查看他们的音乐库或文件。…

使用Qt Creator编写窗体程序并打包发布

1、设置编辑器显示语言为中文(个人习惯) 2、新建窗体应用程序 3、简单修改一下代码 3.1 双击ui文件进入设计模式 3.2 从左侧组件中直接将需要使用的组件拖拽到窗体中 3.3 添加槽函数 选中按钮,右键菜单中 点击 转到槽,头文件和.cpp文件会自动添加对…

中间件_RabbitMQ五种消息模型

文章目录 1.简单消息队列模型2.Work工作队列模型3.发布订阅模型3.1.Fanout广播3.2.Direct路由3.3.Topics通配符 RabbitMQ官方文档 RabbitMQ 提供了5种常用消息模型。但是其实3、4、5这三种都属于订阅模型,只不过进行路由的方式不同。 1.简单消息队列模型 简单消息队…

C语言入门篇——编译篇

目录 1、程序环境 1.1 ANSI C 标准 1.2程序的翻译环境和执行环境 1.3运行环境 2、预处理详解 2.1、预定义符号 2.2、#define 2.2.1#define定义表示符 2.2.2#define定义宏 2.2.3#define替换规则 2.4#和## 2.2.5带副作用的宏参数 2.2.6宏和函数对比 3、#undef 4、…

项目1:登录功能设计

需求 后端接口设计MySQL表常用功能模块 后端总和前端实现方案 home页面 需求 实现一个登录功能 实现的功能 注册(邮箱注册) 登录(邮箱密码) 重置密码 查看操作记录(登录, 注册, 重置密码, 登出. 都算操作) 登出 后端接口设计 1. 人机验证 只要下面出现 人机验证 的功能都需要使…

容器化:MySQL

1 缘起 开启容器化之路。 2 容器化MySQL 2.1 查看MySQL镜像 docker search mysql2.2 指定版本:5.7.30 通过官网查看:https://hub.docker.com/ docker pull mysql:5.7.302.3 路径挂载 容器路径挂载到宿主机。 新建宿主机路径 mkdir -p /home/xind…

ElasticSearch-索引和文档的创建修改删除

目录 一、创建索引 二、查看索引 三、索引是否存在 四、删除索引 五、创建文档 六、查看文档 七、更新文档 八、文档是否存在 九、删除文档 一、创建索引 # 创建一个默认的索引,默认是标准分词器的索引 PUT /es_db2# 创建一个默认为ik分词器的索引 PUT /e…

十万条数据,后端不分页咋办!(如何优化长列表渲染)

十万条数据,后端不分页咋办!(如何优化长列表渲染) 长列表是什么? 我们通常把一组数量级很大的数据叫做长列表,比如渲染一组上千条的数据,我们以数组的形式拿到这些信息,然后遍历渲…

Rust快速安装

Rust依赖C编译,Rust官方推荐的安装方式是利用VisualSudio安装C环境,VisualStuidio用过的都懂,庞大无比、卡顿、下载还贼慢(我当时装了一上午好像),因此我们通过其它方式配置C 安装C环境 下载MinGW 64 win32 seh Mingw官网&…

Java的并发集合框架

文章目录 一、并发集合框架1. 简介2. 接口Iterable2. 接口Collection3. 接口List4. 接口Set5. 接口Queue6. Deque 二、非阻塞队列1. 概念2. ConcurrentHashMap使用(1)简介(2)验证HashMap不是线程安全的(3)验…

ld文件中指定变量在flash中的地址定义

本文说的是在gcc环境中,Keil或IAR可能有自己的使用方法。 我们在定义变量时,有时候需要把变量定义放到flash中的固定位置或区域,此时需要修改工程中的链接文件(link file,ld文件)。 方法一 修改ld文件中的…

软件IIC通信以及源码解析(如何使用)

以对读取MPU6050为例,解析如何采用IIC通信源码。 IIC的的通信,通常三种用途读写。分为: 1: 2: 3: 注:其中最常用的就是1和3了。 对1进行讲解: 指定地址写,通常用作对状…

【网络】- TCP/IP四层(五层)协议 - 网际层(网络层) - IP地址

目录 一、概述 二、IP地址的定义 三、IP地址由网络和主机两部分标识组成 一、概述 上篇文章简单介绍了网际协议IP。网际协议 IP 大致分为三大作用模块, ①IP寻址、 ②路由(最终节点为止的转发) 、③IP分包与组包。 这篇文章主要详细介绍IP地址…

【HTTP协议详解】

目录 1.什么是http2.抓包工具2.1 抓包工具2.2 抓包原理 3.Http协议格式3.1Http请求报文3.2Http响应报文的格式 4.请求报文格式4.1 报文首行4.2 请求报文header 5. 响应报文格式6.构造Http请求7.Https协议7.1 对称密钥7.2 非对称密钥7.3 证书 1.什么是http HTTP全称为“超文本协…

《MySQL是怎么运行的》阅读分享

mysql运行的整体架构简介 Mysql是由两部分构成,一部分是服务器程序,一部分是客户端程序。 服务器程序又包括两部分: 第一部分server层包括连接器、查询缓存、分析器、优化器、执行器等。涵盖 MySQL 的大多数核心服务功能,以及所有…

LeetCode:738.单调递增的数字 714.买卖股票的最佳时机含手续费 968.监控二叉树

738.单调递增的数字 题目 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 贪心 class Solution {public int monotoneIncrea…

数据结构——单链表(C语言)

在这⼀条⼗分漫长的路上&#xff0c;我⾛过阳关⼤道&#xff0c;也⾛过独⽊⼩桥。路旁有深⼭⼤泽&#xff0c;也有平坡宜⼈&#xff1b;有杏花春⾬&#xff0c;也有塞北秋风&#xff1b;有⼭重⽔复&#xff0c;也有柳暗花明&#xff1b;有迷途知返&#xff0c;也有绝处逢⽣。—…

[论文分享] Function Representations for Binary Similarity

Function Representations for Binary Similarity [TDSC 2022] Luca Massarelli , Giuseppe Antonio Di Luna, Fabio Petroni, Leonardo Querzoni, and Roberto Baldoni 二进制相似度问题在于仅考虑两个函数的编译形式来判断它们是否相似。近年来&#xff0c;计算二进制相似度…

【2023 · CANN训练营第一季】进阶班 Atlas 200I DK 智能小车

1 智能小车三维结构设计 1.1 基本模块 坚固酷炫结构模块运动控制模块超声波传感器模块摄像头视觉模块其他传感器模块 1.2 结构设计基本原则 从零开始设计并搭建智能小车&#xff0c;在满足外观要求的基础上&#xff0c;要满足小车运转过程中的运动干涉率为O&#xff0c;并且…

玩转ChatGPT:Show Me插件尝鲜

一、写在前面 之前&#xff0c;不少人问我GPT能否画技术路线图&#xff0c;然后看到了这个插件&#xff1a;Show Me&#xff1a; 简单问GPT这个插件的使用方法&#xff1a; 二、尝鲜过程 &#xff08;1&#xff09;用TA提供的例子试一试&#xff1a; 咒语&#xff1a;请用图…