Java微服务 第二十一章 Java多线程安全与锁

🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄

🌹简历模板、学习资料、面试题库、技术互助

🌹文末获取联系方式 📝

在这里插入图片描述


往期热门专栏回顾

专栏描述
Java项目实战介绍Java组件安装、使用;手写框架等
Aws服务器实战Aws Linux服务器上操作nginx、git、JDK、Vue
Java微服务实战Java 微服务实战,Spring Cloud Netflix套件、Spring Cloud Alibaba套件、Seata、gateway、shadingjdbc等实战操作
Java基础篇Java基础闲聊,已出HashMap、String、StringBuffer等源码分析,JVM分析,持续更新中
Springboot篇从创建Springboot项目,到加载数据库、静态资源、输出RestFul接口、跨越问题解决到统一返回、全局异常处理、Swagger文档
Spring MVC篇从创建Spring MVC项目,到加载数据库、静态资源、输出RestFul接口、跨越问题解决到统一返回
华为云服务器实战华为云Linux服务器上操作nginx、git、JDK、Vue等,以及使用宝塔运维操作添加Html网页、部署Springboot项目/Vue项目等
Java爬虫通过Java+Selenium+GoogleWebDriver 模拟真人网页操作爬取花瓣网图片、bing搜索图片等
Vue实战讲解Vue3的安装、环境配置,基本语法、循环语句、生命周期、路由设置、组件、axios交互、Element-ui的使用等
Spring讲解Spring(Bean)概念、IOC、AOP、集成jdbcTemplate/redis/事务等

系列文章目录

第一章 Java线程池技术应用
第二章 CountDownLatch和Semaphone的应用
第三章 Spring Cloud 简介
第四章 Spring Cloud Netflix 之 Eureka
第五章 Spring Cloud Netflix 之 Ribbon
第六章 Spring Cloud 之 OpenFeign
第七章 Spring Cloud 之 GateWay
第八章 Spring Cloud Netflix 之 Hystrix
第九章 代码管理gitlab 使用
第十章 SpringCloud Alibaba 之 Nacos discovery
第十一章 SpringCloud Alibaba 之 Nacos Config
第十二章 Spring Cloud Alibaba 之 Sentinel
第十三章 JWT
第十四章 RabbitMQ应用
第十五章 RabbitMQ 延迟队列
第十六章 spring-cloud-stream
第十七章 Windows系统安装Redis、配置环境变量
第十八章 查看、修改Redis配置,介绍Redis类型
第十九章 Redis RDB AOF
第二十章 Spring boot 操作 Redis
第二十一章 Java多线程安全与锁


文章目录

  • 往期热门专栏回顾
  • 系列文章目录
  • 前言
  • 1、Java多线程安全与锁
    • 1.1、多线程安全问题
    • 1.2、线程安全问题三方面
      • 1.2.1、共享数据在jvm中的表现
      • 1.2.2、内存抽象模型
    • 1.3、Java中的锁
      • 1.3.1、同一个JVM内锁
        • 1.3.1.1、synchronized 关键字
        • 1.3.1.2、volatile关键字
        • 1.3.1.3、volatile和synchronized区别
        • 1.3.1.4、volatile能替代synchronized吗


前言

本章节介绍Java多线程安全与锁

1、Java多线程安全与锁

1.1、多线程安全问题

当多个线程同时操作同一个数据时,可能会出现数据不一样的情况,这就是线程安全问题。线程安全机制用于保证多个线程访问数据时的一致性.

1.2、线程安全问题三方面

  • 原子性
    一个线程对数据的操作对于其他的线程来说是原子的,要么操作完成,要么什么也没做;当一个线程在操作数据时,不允许其他的线程参与.
  • 可见性
    线程对共享数据的访问是否对其他的线程可见
  • 有序性
    指令重排序与内存重排序指令重排序是指CPU执行指令的顺序与程序的顺序可能不一样; 内存重排序是指内存访问顺序与感知顺序可能不一样。

1.2.1、共享数据在jvm中的表现

在这里插入图片描述

  • 每个线程都有独立的线程栈,不能相互访问线程栈的内容
  • 所有线程都能访问堆中的数据
  • 多个线程访问同事访问一个对象实例或者静态变量时,会出现安全问题。

1.2.2、内存抽象模型

在这里插入图片描述

  • 每个线程都有自己独立的工作内存
  • 线程1无法访问线程2的工作内存
  • 线程在访问共享数据时,会把主内存中的共享变量复制到自己的工作内存中,线程操作的是工作内存中数据的副本

1.3、Java中的锁

(多线程并发操作同一个数据可能会引发线程安全问题)
锁就是把多个线程对数据的并发操作转换为串行操作。
在这里插入图片描述

1.3.1、同一个JVM内锁

1.3.1.1、synchronized 关键字
  • 同步代码块
  • 同步实例方法
  • 同步类的静态方法
    当线程释放锁时,JMM(java内存模型)会把该线程对应的工作内存中的共享变量刷新到主内存中。当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内 存中读取共享变量。

**代码 使用synchronized **

package com.xxxx.reids.thread;

import lombok.AllArgsConstructor;
import lombok.Data;

/***
 * @title Account
 * @desctption 账户
 * @author Kelvin
 * @create 2023/5/29 11:43
 **/
@Data
@AllArgsConstructor
public class Account {

    /**
     * 余额
     */
    private Integer balance;

}

package com.xxxx.reids.thread;

import lombok.AllArgsConstructor;
import lombok.Data;

/***
 * @title Person
 * @desctption 人员信息
 * @author Kelvin
 * @create 2023/5/29 11:44
 **/
@Data
@AllArgsConstructor
public class Person {

    /**
     * 姓名
     */
    private String name;
    /**
     * 取款金额
     */
    private Integer drawAccount;

}
package com.hqyj.reids.thread;

import java.util.concurrent.locks.Lock;

/***
 * @title Draw
 * @desctption 取款
 * @author Kelvin
 * @create 2023/5/29 11:49
 **/
public class Draw implements Runnable{
    private Account account;
    private Person person;

    public Draw(Account account,Person person){
        this.account = account;
        this.person = person;
    }
    
    @Override
    public void run() {
   		 synchronized (this.account){
            while (true){
                //余额
                Integer balance = this.account.getBalance();
                if(balance < this.person.getDrawAccount()){
                    //余额不足
                    System.out.println(this.person.getName() + ",账户余额不足");
                    break;
                }else{
               /* try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
                    //取款,减余额
                    this.account.setBalance(balance - this.person.getDrawAccount());
                    System.out.println(this.person.getName() + "取了"+this.person.getDrawAccount()+"钱,账户余额:" + this.account.getBalance());
                }
            }
        }
        
    }
}

测试

package com.xxxx.redis;

import com.xxxx.reids.thread.Account;
import com.xxxx.reids.thread.Draw;
import com.xxxx.reids.thread.Person;
import org.junit.Test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/***
 * @title ThreadLockTest
 * @desctption 测试类
 * @author Kelvin
 * @create 2023/5/29 11:50
 **/
public class ThreadLockTest {

    @Test
    public void lock() {

        Lock lock = new ReentrantLock();

        Account account = new Account(100);
        Person zhangSan = new Person("张三", 20);
        Person liSi = new Person("李四", 15);

 		new Thread(new Draw(account,zhangSan)).start();
        new Thread(new Draw(account,liSi)).start();

    }

}

在这里插入图片描述

**代码2 使用Lock **

package com.hqyj.reids.thread;

import java.util.concurrent.locks.Lock;

/***
 * @title Draw
 * @desctption 取款
 * @author Kelvin
 * @create 2023/5/29 11:49
 **/
public class Draw implements Runnable{
    private Account account;
    private Person person;
    private Lock lock ;

    public Draw(Account account,Person person,Lock lock){
        this.account = account;
        this.person = person;
        this.lock = lock;
    }

    @Override
    public void run() {
        lock.lock();
        //取款操作
        Integer drawAccmount = this.person.getDrawAccount();
        Integer balanceInit = this.account.getBalance();
        //System.out.print("原始值:" + balanceInit + " | ");
        if(balanceInit  < drawAccmount){
            //余额不足,不给取款
            System.out.println("取款余额不足");
            //break;
        }else{
            Integer balance = balanceInit - drawAccmount;
            this.account.setBalance(balance);
            System.out.println(this.person.getName() + "成功取款:" + drawAccmount + ",账户余额:" + balance);
        }

        lock.unlock();
    }
}

import com.xxxx.reids.thread.Account;
import com.xxxx.reids.thread.Draw;
import com.xxxx.reids.thread.Person;
import org.junit.Test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/***
 * @title ThreadLockTest
 * @desctption 测试类
 * @author Kelvin
 * @create 2023/5/29 11:50
 **/
public class ThreadLockTest {

    @Test
    public void lock() {

        Lock lock = new ReentrantLock();

        Account account = new Account(110);
        Person zhangSan = new Person("张三", 17);
        Person liSi = new Person("李四", 12);

        new Thread(new Draw(account,zhangSan,lock)).start();
        new Thread(new Draw(account,liSi,lock)).start();
        new Thread(new Draw(account,zhangSan,lock)).start();
        new Thread(new Draw(account,liSi,lock)).start();
        new Thread(new Draw(account,zhangSan,lock)).start();
        new Thread(new Draw(account,liSi,lock)).start();
        new Thread(new Draw(account,zhangSan,lock)).start();
        new Thread(new Draw(account,liSi,lock)).start();

    }

}

在这里插入图片描述

死锁的例子
Java 死锁产生的四个必要条件:

  • 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
  • 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  • 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
  • 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
/***
 * @title DieLock
 * @desctption 死锁案例
 * @author Kelvin
 * @create 2023/5/29 15:36
 **/
import java.util.concurrent.TimeUnit;

public class DieLock {
    private static Object object1 = new Object();
    private static Object object2 = new Object();
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                synchronized (object1){
                    System.out.println("Thread1 get object1");
                    try {
                        TimeUnit.MILLISECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (object2){
                        System.out.println("thread1 get object2");
                    }
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                synchronized (object2){
                    System.out.println("Thread2 get object2");
                    try {
                        TimeUnit.MILLISECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (object1){
                        System.out.println("thread2 get object1");
                    }
                }
            }
        }.start();
    }
}

在这里插入图片描述

1.3.1.2、volatile关键字

volatile是java虚拟机提供的轻量级的同步机制

  • 保证可见性
  • 不保证原子性
  • 保证有序性

什么时候去使⽤Volatile?

  • 某个属性被多个线程共享,其中有⼀个线程修改了此属性,其他线程可以⽴即得到修改后的值,⽐如作为触发器,状态量标记,实现轻量级同步
  • volatile可以在单例双重检查中实现可⻅性和禁⽌指令重排序,可以解决单例双重检查对象初始化代码执⾏乱序问题,从⽽保证安全性。
public class SingletonObject {
    private static volatile SingletonObject singletonObject = new SingletonObject();
    private SingletonObject(){
    }

    public static synchronized SingletonObject getInstance(){
        if(singletonObject == null){
            singletonObject = new SingletonObject();
        }
        return singletonObject;
    }
}
1.3.1.3、volatile和synchronized区别
  • volatile只能修饰实例变量和类变量,只能作⽤于属性,⽽synchronized可以修饰⽅法和代码块。
  • volatile保证数据的可⻅性,⽤于禁⽌指令重排序,但是不保证原⼦性;⽽synchronized是⼀种互斥的机制。
  • volatile属性的读写操作都是⽆锁的,不需要花费时间在获取锁和释放锁上,所以说它是低成本的
  • volatile可以看做是轻量版的synchronized,volatile不保证原⼦性,但是如果是对⼀个共享变量进⾏多个线程的赋值,⽽没有其他的操作,那么就可以⽤volatile来代替synchronized,因为赋值本身是有原⼦性的,⽽volatile⼜保证了可⻅性,所以就可以保证线程安全了。
1.3.1.4、volatile能替代synchronized吗
  • volatile属性的读写操作都是⽆锁的,
  • 它没有提供原⼦性和互斥性,它不能替代synchronized

资料获取,更多粉丝福利,关注下方公众号获取

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

​​力矩电机的建模、仿真与选择

1 力矩电机建模 图1中 e i ( t ) {e_i}\left( t \right) ei​(t)为电机输入电压&#xff0c; L a {L_a} La​为电枢电感&#xff0c; R a {R_a} Ra​为电枢电阻&#xff0c; e m ( t ) {e_m}\left( t \right) em​(t)为电机工作时产生的反电动势&#xff0c; i a ( t ) {i_a}\l…

python爬虫(6)之处理数组

1、拆分数组 1、spilt&#xff08;&#xff09;函数 此函数的用处是将数组均分成几个数组 演示如下&#xff1a; import numpy as np ac np.array([1,2,8,9,3,5,5,8]) ac1 np.split(ac,2) ac2 np.split(ac,[3,6]) print(ac1,ac2) 结果如下&#xff1a; 其中若是一个数…

[java——基础] 双亲委派机制

目录 核心思想&#xff1a; 双亲委派机制的好处&#xff1a; 三种类加载器 解析源代码 双亲委派思想面试总结&#xff1a; 核心思想&#xff1a; 向上搜索&#xff0c;向下加载。 双亲委派机制的好处&#xff1a; 防止Java核心类被篡改&#xff0c;防止类的重复加载。 三…

力扣--动态规划/深度优先算法/回溯算法93.复原IP地址

这题主要用了动态规划和回溯算法。 动态规划数组初始化&#xff08;DP数组&#xff09;: 首先&#xff0c;创建一个二维数组dp&#xff0c;用于记录字符串中哪些部分是合法的IP地址。对字符串进行遍历&#xff0c;同时考虑每个可能的IP地址部分&#xff08;每部分由1到3个字符组…

基于深度学习的鱼类分类检测系统(含UI界面、yolov8、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8 yolov8主要包含以下几种创新&#xff1a;         1. 可以任意更换主干结构&#xff0c;支持几百种网络主干。 数据集&#xff1a;     网上下载的数据集&#x…

算法刷题day28

目录 引言一、截断数组二、双端队列三、日期统计 引言 这几道题是周赛里的几道题目&#xff0c;第一道题目我没用这种方法&#xff0c;但还是做出来了&#xff0c;用的一种比较特殊的思考方法&#xff0c;就是把每一个点都判断出来&#xff0c;不满足要求的就舍弃&#xff0c;…

【论文解读】多模态大语言模型综述

目录 一、简要介绍 二、概要 三、方法 3.1.多模态指令调整 3.1.1介绍 3.1.2初步研究 3.1.3模态对齐 3.1.4数据 3.1.5模态桥接 3.1.6评估 3.2多模态的上下文学习 3.3.多模态的思维链 3.3.1模态桥接 3.3.2学习范式 3.3.3链配置 3.3.4生成模式 3.4.LLM辅助视觉推理…

(golang)切片何时会创建新切片或影响原切片

什么时候切片操作会影响原切片 // 1.切片后没有触发slice的扩容机制时 什么时候对切片操作会创建新切片不影响原切片 // 2.对切片头元素进行截取的时候 // 3.当使用append时&#xff0c;len > cap则会触发扩容机制 前置&#xff1a; //slice结构体 type SliceHeader struct…

【你也能从零基础学会网站开发】Web建站之javascript入门篇 JavaScript事件处理

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;程序猿、设计师、技术分享 &#x1f40b; 希望大家多多支持, 我们一起学习和进步&#xff01; &#x1f3c5; 欢迎评论 ❤️点赞&#x1f4ac;评论 &#x1f4c2;收藏 &#x1f4c2;加关注 什么是DHTML …

宏工科技数智方案现先进陶瓷展,VR体验数字工厂引关注

3月6-8日&#xff0c;第十六届中国国际粉末冶金、硬质合金与先进陶瓷展览会在上海举行。本届展会&#xff0c;宏工科技股份有限公司携VR体验数字工厂和正负压气力输送系统惊艳亮相&#xff0c;“现实虚拟”的呈现方式收获众多行业客户及专业观众高度关注。 展会汇聚了来自粉末冶…

【Python】一文详细介绍 plt.rc_context() 在 Matplotlib 中的原理、作用、注意事项

【Python】一文详细介绍 plt.rc_context() 在 Matplotlib 中的原理、作用、注意事项 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&a…

在funtion中用分号间隔还是逗号间隔

问: 回答: 这段代码是一个Vue组件方法的实现&#xff0c;名为resetForm。该方法的主要作用是关闭一个对话框&#xff08;通过设置this.dialogFormVisible false&#xff09;&#xff0c;重置表单字段&#xff08;使用this.$refs[formName].resetFields();&#xff09;&#x…

动态规划课堂5-----子序列问题(动态规划 + 哈希表)

目录 引言&#xff1a; 例题1&#xff1a;最长递增子序列 例题2&#xff1a;最长定差子序列 例题3&#xff1a;最长的斐波那契子序列的长度 例题4&#xff1a;最长等差数列 例题5&#xff1a;等差数列划分II-子序列 结语&#xff1a; 引言&#xff1a; 要想解决子序列问…

如何有效利用代理IP保护隐私并突破网络限制

有效利用代理IP保护隐私并突破网络限制通常涉及以下几个步骤和注意事项&#xff1a; 1. 选择合适类型的代理&#xff1a; - 高度匿名代理&#xff1a;这是最佳选择&#xff0c;因为它不会在HTTP头部透露任何关于你是通过代理访问的信息&#xff0c;因此可以最大程度地保护你的原…

二维卡通数字人解决方案

美摄科技&#xff0c;凭借在数字人领域的深厚积累与不断创新&#xff0c;为企业量身定制了一套高效、灵活的二维卡通数字人解决方案&#xff0c;助力企业打造独具特色的品牌形象&#xff0c;提升用户互动体验。 美摄科技的二维卡通数字人解决方案&#xff0c;以Live 2D等先进工…

JS 事件捕获、事件冒泡、事件委托

js事件机制在开发中可以说时刻使用&#xff0c;例如dom绑定事件、监听其自身事件等。js事件机制有事件捕获、事件冒泡俩种机制&#xff0c;我们分别说下这俩种机制的使用场景。 一、概念 事件捕获顺序如下&#xff1a; window > document > body > div 事件冒泡顺序…

Codeforces Round 933 (Div. 3)C:Rudolf and the Ugly String

题目链接&#xff1a;Dashboard - Codeforces Round 933 (Div. 3) - Codeforces 解题思路&#xff1a; 解题思路&#xff1a; 题目大概意思是字符串中最少去掉几个单词可以使字符串变漂亮&#xff0c;其实只要找“map"和”pie“这两个单词数量&#xff0c;注意判断&quo…

32个关键字详解①(C语言)

目录 关键字分类&#xff1a; 第一个C程序 - 补充内容 变量的定义与声明 - 补充内容 变量的分类 - 补充内容 变量的作用域 - 补充内容 变量的生命周期 - 补充内容 auto 关键字 register 关键字 static 关键字 static 修饰变量&#xff1a; static修饰函数 sizeof 关键字 基本数…

罐头鱼AI短视频矩阵营销|视频批量剪辑|矩阵系统

AI批量视频剪辑系统是一款功能丰富的视频编辑软件&#xff0c;提供了以下主要功能&#xff1a; 首页显示&#xff1a;在首页上显示用户的登录状态、已绑定的账号数量以及最近上传的视频素材和新上传素材列表。 抖音账号绑定功能&#xff1a;用户可以绑定抖音账号&#xff0c;Q…

读书笔记之《人工智能全球格局》:人工智能时代的中国之路

《人工智能全球格局&#xff1a;未来趋势与中国位势》的作者:是国务院发展研究中心国际技术经济研究所 / 中国电子学会 / 智慧芽&#xff0c; 2019年出版。 这本书全面深入地探讨了人工智能&#xff08;AI&#xff09;的发展历程、当前状态、未来趋势以及中国在这一领域的地位和…