java多线程同步技术基础

news/2024/10/31 1:21:51/

说明

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

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

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

在这里插入图片描述

二、锁方法

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

/*** 抢红包*/
class GetBonus implements Runnable {/*** 创建一个红包数量*/private static int bonusQuantity = 3;@Overridepublic void run() {get();}/*** 将抢红包的代码抽取成一个方法,并用synchronized修饰*/private synchronized void get() {// 如果红包数量大于0if (bonusQuantity > 0) {System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");// 红包数量-1bonusQuantity--;} 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();@Overridepublic void run() {// 加锁lock.lock();// 如果红包数量大于0if (bonusQuantity > 0) {System.out.println("恭喜" + Thread.currentThread().getName() + "您成功抢到一个20元的红包");// 红包数量-1bonusQuantity--;} 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.ppmy.cn/news/83087.html

相关文章

Python实现ACO蚁群优化算法优化卷积神经网络回归模型(CNN回归算法)项目实战

说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蚁群优化算法(Ant Colony Optimization, ACO)是一种源于大自然生物世界的新的仿生进化算法&#xff0c…

深入学习MYSQL-使用触发器

触发器 每个表最多支持6个触发器,(insert,update,delete)之前和之后。 删除触发器 drop trigger trigger_name;insert 触发器  在INSERT触发器代码内,可引用一个名为NEW的虚拟表,访问被插入…

API测试| 了解API接口测试| API接口测试指南

什么是API? API是一个缩写,它代表了一个 pplication P AGC软件覆盖整个房间。API是用于构建软件应用程序的一组例程,协议和工具。API指定一个软件程序应如何与其他软件程序进行交互。 例行程序:执行特定任务的程序。例程也称为过…

搭建监控日志系统

在微服务或者集群架构中,一次请求的调用会跨多个服务(web,mysql,feign等)、多个模块(用户模块,商品模块等)、多个容器(用户模块可能有多个实例),这…

Unity之OpenXR+XR Interaction Toolkit 安装和配置

前言 XR Interaction Toolkit 是Unity基于OpenXR标准,发布的一套XR工具,目的是方便我们快速接入XR相关的SDK,并且做到兼容不同VR设备的目的,目前流行的VR设备如Oculus,Metal,HTC Vive,Pico等统统都支持。 所以我们今天的目的就是把XR Interaction Toolkit从导入到配置,…

Elastic Learned Sparse Encoder 简介:Elastic 用于语义搜索的 AI 模型

作者:Aris Papadopoulos, Gilad Gal 寻找意义,而不仅仅是文字 我们很高兴地与大家分享,在 8.8 中,Elastic 提供开箱即用的语义搜索。语义搜索旨在根据文本的意图或含义进行搜索,而不是词汇匹配或关键字查询。与传统的…

C++11 call_once 和 once_flag

文章目录 一、简介二、原理2.1 示例2.2 call_once源码详解2.3 once_flag源码详解 三、Linux内核中的 DO_ONCE 机制 一、简介 std::call_once 和 std::once_flag 是 C11 中引入的线程安全的函数和类型,用于确保某个函数只被调用一次。 std::once_flag 是一个类型&a…

SpringBoot + Docker 实现一次构建到处运行

一、容器化部署的好处 Docker 作为一种新兴的虚拟化方式,它可以更高效的利用系统资源,不需要进行硬件虚拟以及运行完整操作系统等额外开销。 传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行宿主内…