JavaEE初阶之线程安全(一)

 

目录

题外话

正题

1.线程调度是随机的

2.修改共享数据

知识点

线程同步机制

线程异步机制

举例说明

synchronized()

知识点

举例说明

举例代码详解

死锁

举个例子:

代码

小结


题外话

这两天忽冷忽热的感冒了,昨天状态特别不好断更了一天,今天继续加油!

我会把所有知识点放在每个小标题最前面,省的大家东找一个西找一个,大家可以先略微看看知识点,把文章看一遍回过头再看知识点,就有种豁然开朗的感觉

然后我比较喜欢用费曼学习法,大家有兴趣可以查查

这里简单说下费曼学习法,大家应该也或多或少知道这个,大家在跟着视频学习的时候,可以附和视频中的老师,或者试着对话,再或者遇到知识点,可以用给别人讲课的方式,把知识点说出来,这样确实很温故知新

就是在宿舍自言自语,自我对话会被别人当成傻子,怕社死的还是慎重考虑

我觉得做笔记也很关键,我从开始学javase,就开始把所有java知识点都放在了一个word文档里

复习起来也很方便,不需要再去翻找视频什么的费时费力,(如下图)

正题

我们先说说引发线程安全的因素

1.线程调度是随机的

大家都知道,JVM中线程是抢占式调度,所有线程去抢占cup的资源,线程抢的资源越多,执行该线程的速度也会越快

我们要做的是在写代码的时候,使多线程让任意执行顺序下都可以顺利执行

2.修改共享数据

知识点

一.先提出两个概念

我们把线程排队执行(线程一个一个执行),叫做;线程同步机制

而抢占式执行(多个线程一起执行),叫做;线程异步机制

先说说线程同步机制和线程异步机制的优缺点

线程同步机制

优点:可以保证数据安全问题

缺点:因为排队执行,导致效率变低,而且利用synchronized也会产生一些问题(如死锁等等)

线程异步机制

优点:多线程抢占式执行,效率很高

缺点:因为线程各自独立运行,互不影响,修改共享数据会产生数据错乱问题

二.再说说在java中什么数据会产生修改共享数据的问题

1.一般情况下:局部变量不存在线程安全问题。(尤其是基本数据类型不存在线程安全问题,因为它们在栈中,栈不共享数据,如果是引用数据类型,就另说了)

2.实例变量可能存在线程安全问题,实例变量在堆中,堆是多线程共享的。

3.静态变量也可能存在线程安全问题,静态变量在堆中,堆是多线程共享的

举例说明

先让大家更清晰理解修改共享数据会产生什么问题

比如一个银行账户被张三和李四两个人使用,两个人都把这张卡绑定支付,两个人都想取钱出来花,(如下图)

首先,银行会各自读取银行卡余额,然后再各自取钱,按照JVM中线程抢占式调用资源,

我们不知道张三取钱的时候,李四会不会才进行到获取银行卡余额这种情况,如果发生了这种情况,就会出现银行卡余额数据异常(当然,银行肯定非常安全,这种问题早就被解决了)

上述问题就是修改共享数据

所以面对这种问题,我们并不需要抢占式执行,而是需要排队执行,给张三取完钱,再去执行李四取钱的操作.

我们就需要synchronized()

synchronized()

知识点

1.线程同步的本质是:线程排队执行就是同步机制。

2. 语法格式:

synchronized(必须是需要排队的这几个线程共享的对象){

需要同步的代码

}

“必须是需要排队的这几个线程其享的对象”这个必须选对,

这个如果选错了,可能会无故增加同步的线程数量,导致效率降低。

3.可以给一块代码加锁,也可以对实例方法,静态方法加锁

加锁并不是一个很好的选择

加锁优缺点

优点:防止修改共享数据造成数据错乱

缺点:1.加锁的位置需要很慎重考虑

    因为有些线程需要修改共享数据,而有些线程不需要修改共享数据,如果把这种数据全部加锁,就会产生效率问题

        2.加锁占用资源比较大

        3.当我们写的代码越来越多,如果滥用锁的话会产生死锁等等一系列问题,导致效率很低

举例说明

synchronized()就是锁,大家可以理解为上厕所

一套屋子只有一个厕所,有人进去了,其他人只能等他出来才能进去

通过一段代码,让大家更清晰理解上锁会怎么样

这里代码没有上锁,让大家看看修改共享数据会造成什么后果

举例代码详解

以下是实现两个线程各自执行一万次count累加

public class ThreadDemo14 {
     static int count=0;
    public  void add()
    {

        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadDemo14 t=new ThreadDemo14();
        Thread t1=new Thread(()->{
            for (int i = 0; i < 10000; i++) {
                t.add();
            }
        });
        Thread t2=new Thread(()->{
            for (int i = 0; i < 10000; i++) {
               t.add();
            }
        });
        t1.start();
        t2.start();
        Thread.sleep(1000);
        System.out.println(count);
    }

}
正常来说我们理想状态打印出的count应该是20000,但是修改共享数据会发生数据异常(如下图)



这次大家看好我加锁后的代码
public class ThreadDemo14 {
     static int count=0;
//直接给add方法加锁
    public synchronized void add()
    {

        count++;
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadDemo14 t=new ThreadDemo14();
        Thread t1=new Thread(()->{
            for (int i = 0; i < 10000; i++) {
                t.add();
            }
        });
        Thread t2=new Thread(()->{
            for (int i = 0; i < 10000; i++) {
               t.add();
            }
        });
        t1.start();
        t2.start();
        Thread.sleep(1000);
        System.out.println(count);
    }

}

而这次运行结果和咱们理想结果一样

 

 

我是在add方法上直接加锁了,当然也可以在方法里加锁,synronized(this){代码块}这种形式即可

死锁

我们再说一下死锁的问题,其实死锁就是互相矛盾

举个例子:

小明在厕所1,厕所1只能洗手,一个厕所只能进入一个人

小红在厕所2,厕所2只能蹲坑,一个厕所只能进入一个人

小明洗完手想去蹲坑,而小红蹲完坑想去洗手,但是一个厕所只能进入一个人,这样他们永远不能走出自己所在的厕所,如下图

让大家看看代码,或许就明白了!

代码
public class ThreadDemo15 {
    public static void main(String[] args) {
//创建两个Oberject对象o1,o2
        Object o1=new Object();
        Object o2=new Object();
//建立两个线程t1,t2
        Thread t1=new MyThread01(o1,o2);
        Thread t2=new MyThread01(o1,o2);
//t1命名t1,t2命名t2
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}
//创建类继承Thread
class MyThread01 extends Thread
{
//创建私密变量o1,o2
    private Object o1;
    private Object o2;
//创建构造方法
    public MyThread01(Object o1,Object o2)
    {
        this.o1=o1;
        this.o2=o2;
    }
//重写run方法
    @Override
    public void run() {
//如果线程名字等于t1
        if ("t1".equals(Thread.currentThread().getName()))
        {
//进入o1锁
            synchronized (o1)
            {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
        System.out.println("解开了");
//进入o2锁
                synchronized (o2)
                {
                    
                }
            }
        }
//如果线程名字和t2一样
       else if ("t2".equals(Thread.currentThread().getName()))
        {
//进入o2锁
            synchronized (o2)
            { try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
              System.out.println("解开了");
//进入o1锁
                synchronized (o1)
                {
                    
                }
            }
        }
    }
}

当我们运行的时候就会发现,t1和t2都进入了锁中,但是永远不会结束进程,永远不会报错,就这样一直锁着,如下图

所以说还是需要好好掌握一下synchronized()的使用方式

小结

写完这篇有点晚了,但今天收获还是很大的,喜欢的朋友多多支持一下吧!

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

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

相关文章

【RT_Thread】---stm32f407zgt6使用env配置工程

用rt_thread env配置工程 1. git rt_thread 源码 2.找到对应芯片厂家扳机支持包 3 重新命名一个自己项目的工程 4 打开env 配置驱动 具体参考官方&#xff1a;Env 用户手册 (rt-thread.org) 5 修改路径为英文 6 修改完boad init 就应该可以用了(还有系统时钟不然会有问题)…

基于stm32的h5的spi屏幕调试

基于stm32的h5的spi屏幕调试 本文目标&#xff1a;基于stm32的基础实验 按照本文的描述&#xff0c;应该可以跑通实验并举一反三。 先决条件&#xff1a;装有编译和集成的开发环境&#xff0c;比如&#xff1a;Keil uVision5、STM32CubeMX 使用外设&#xff1a;GPIO、SPI …

Redis中的复制功能(一)

复制 概述 在Redis中&#xff0c;用户可以通过执行SLAVEOF命令或者设置slaveof选项&#xff0c;让一个服务器去复制(replicate)另一个服务器&#xff0c;我们称呼被复制的服务器为主服务器(master)&#xff0c;而对主服务器进行复制的服务器则被称为从服务器(slave),如图所示…

Python 全栈体系【四阶】(十八)

第五章 深度学习 一、基本理论 4. 神经网络的改进 4.1 神经网络的局限 全连接神经网络的局限&#xff08;一&#xff09; 未考虑数据的“形状”&#xff0c;会破坏数据空间结构。例如&#xff0c;输入数据是图像时&#xff0c;图像通常是高长通道方向上的 3 维形状。但是&a…

基于springboot+vue实现的房源出租信息系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

Java学习之类和对象、内存底层

目录 表格结构和类结构 表格的动作和类的方法 与面向过程的区别 具体实现 对象和类的详解 类的定义 属性&#xff08;field 成员变量&#xff09; 方法 示例--编写简单的学生类 简单内存分析(理解面向对象) 构造方法(构造器 constructor) 声明格式&#xff1a; 四…

深入探究Shrio反序列化漏洞

Shrio反序列化漏洞 什么是shrio反序列化漏洞环境搭建漏洞判断rememberMe解密流程代码分析第一层解密第二层解密2.1层解密2.2层解密 exp 什么是shrio反序列化漏洞 Shiro是Apache的一个强大且易用的Java安全框架,用于执行身份验证、授权、密码和会话管理。使用 Shiro 易于理解的…

FittenChat:程序员写代码的最好辅助利器,没有之一

&#x1f341; 作者&#xff1a;知识浅谈&#xff0c;CSDN签约讲师&#xff0c;CSDN博客专家&#xff0c;华为云云享专家&#xff0c;阿里云专家博主 &#x1f4cc; 擅长领域&#xff1a;全栈工程师&#xff0c;大模型&#xff0c;爬虫、ACM算法 &#x1f492; 公众号&#xff…

如何评估基于指令微调的视觉语言模型的各项能力-MMBench论文解读

1. 传统基准的固有局限 VQAv2:视觉问题回答数据集,主要用于评估视觉理解与推理能力。COCO Caption:图像描述生成数据集,用于评估模型对图像内容的理解与描述能力。GQA:结合常识的视觉问题回答数据集。OK-VQA:需要外部知识的视觉问题回答数据集。TextVQA:图像中包含文本的…

Linux系统网络的实时性评估

目录 1.使用 cyclictest 测试系统实时性2.测试系统通信实时性2.1 PingPlotter2.2 使用 ping 测试通讯实时性 3. 使用 iperf 测试带宽4.网络性能测试 1.使用 cyclictest 测试系统实时性 安装cyclictest sudo apt-get update sudo apt-get install rt-testscyclictest -p 99 -i…

TS学习01 基本类型、编译选项、打包ts代码

TS学习 TypeScript00 概念01 开发环境搭建02 基本类型基本使用⭐类型 03 编译选项tsconfig.jsoncompilerOptions语法检查相关 04 webpack打包ts代码错误解决 05 babel TypeScript BV1Xy4y1v7S2学习笔记 00 概念 以 JavaScript 为基础构建的语言 一个 JavaScript 的超集 Type…

如何使用KST指标进行多头交易,Anzo Capital一个条件设置

在之前的文章中&#xff0c;我们进行分享了以下知识&#xff1a;什么是KST指标&#xff0c;以及如何进行计算KST指标。有聪明的投资者就在后台进行咨询Anzo Capital昂首资本了&#xff0c;我们知道这些知识有什么用呢&#xff1f; 当然有用了&#xff0c;只要理解背后的逻辑知…

三层架构实验--对抗遗忘

交换配置顺序&#xff1a; channel vlan Trunk stp svi vrrp dhcp 绑定channel [sw1]interface e [sw1]interface Eth-Trunk 0 [sw1-Eth-Trunk0]int g 0/0/22 [sw1-GigabitEthernet0/0/22]eth-trunk 0 [sw1-GigabitEthernet0/0/23]eth-trunk 0 [sw2]interface Eth…

【语言信号增强算法研究-1】维纳滤波(Wiener Filter)

1 语音增强方法分类 2 维纳滤波的局限性 对于非线性和非高斯噪声的处理效果不佳&#xff1b; 对于信号和噪声的统计特性要求比较高&#xff0c;需要准确地了解信号和噪声的分布规律&#xff08;说明自适应很差&#xff09;&#xff1b; 在处理复杂信号时&#xff0c;需要进行多…

副业赚钱攻略:给工资低的你6个实用建议,闷声致富不是梦

经常有朋友向我咨询&#xff0c;哪些副业比较靠谱且能赚钱。实际上&#xff0c;对于大多数打工族而言&#xff0c;副业不仅是增加收入的途径&#xff0c;更是利用业余时间提升自我、实现价值的重要方式。 鉴于此&#xff0c;今天我想和大家分享六个值得尝试的副业&#xff0c;…

sql注入---Union注入

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 学习目标 了解union注入过程中用到的关键数据库&#xff0c;数据表&#xff0c;数据列sql查询中group_concat的作用使用union注入拿到靶机中数据库里的所有用户名和密码 一. 获得数据库表名和列…

CDN流量清洗

CDN是构建在网络之上的内容分发网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c;降低网络拥塞&#xff0c;提高用户访问响应速度和命中率&#xff0c;因此CDN也用到了负载均衡技术。…

57、FreeRTOS/串口通信和DMA ADC PWM相关20240401

一、使用PWMADC光敏电阻完成光控灯的实验。&#xff08;根据测得的光敏电阻大小&#xff0c;控制灯的亮度&#xff09; 代码&#xff1a; /* USER CODE BEGIN 2 */HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_3);//打开定时器的PWM通道3HAL_TIM_PWM_Start(&htim3,TIM_CHANN…

如何写好面向新人的开发文档

前言 大家在进入公司的时候&#xff0c;或多或少会接触到公司或者来自前辈的文档。文档水平层次不齐&#xff0c;好的事无巨细&#xff0c;层次清晰&#xff0c;拉跨的可能就像正确的废话&#xff0c;正确的说了正确的话。文档形式也是多种多样&#xff0c;word、markdown、云…

【学习笔记】java项目—苍穹外卖day06

文章目录 苍穹外卖-day06课程内容1. HttpClient1.1 介绍1.2 入门案例1.2.1 GET方式请求1.2.2 POST方式请求 2. 微信小程序开发2.1 介绍2.2 准备工作2.3 入门案例2.3.1 小程序目录结构2.3.2 编写和编译小程序2.3.3 发布小程序 3. 微信登录3.1 导入小程序代码3.2 微信登录流程3.3…