当前位置: 首页 > news >正文

线程安全性问题(一)

✨个人主页: 不漫游-CSDN博客

前言

在上次讲线程状态的文章里提到了BLOCKED状态--->http://t.csdnimg.cn/T17sQ

因为线程 对于 锁 的竞争引起的堵塞。接着详细展开讲解~

什么是线程安全性问题?

话不多说,先看一段代码:

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

分析代码,发现使用两个线程t1 t2 来累加 数组 count[0] 的值,在不运行的情况下, 是不是觉得结果是20000.

但实际运行结果却是这样:

等等。

每次都是随机值,很明显这就是一个bug。

原因

1.线程在操作系统中,随机调度,抢占式执行。

这是根本原因。 

2.多个线程,同时修改同一个变量

3.修改操作不是原子的。

首先了解一下什么是原子性? 

它指的是一个操作是不可分割的,要么完全执行,要么完全不执行。

在多线程环境中,如果一个操作是原子的,那么多个线程同时执行这个操作时,不会出现竞争条件或数据不一致的问题。

但是!!!​count[0]++​ 这个操作在Java中不是原子操作。它实际上包含以下三个步骤:

1.读取 ​count[0]​ 的当前值。
2.将读取的值加1。
3.将新值写回 ​count[0]​。


如果两个线程同时执行这些步骤,可能会发生以下情况:

线程t1读取 ​count[0]​ 的值(假设已经到100)。
线程t2也读取 ​count[0]​ 的值(仍然是100)。
线程t1将读取的值加1(得到101)并写回。
线程t2也将读取的值加1(得到101)并写回。
结果是 ​count[0]​ 的值只增加了1,而不是2,因为两个线程都基于相同的初始值进行了递增操作。

这还是只是一种情况,而且因为原因1中的随机调度和抢占式执行。两个线程的执行顺序几乎是随机的 。也就导致了每次执行的结果都是不一样的。

这也就导致了线程安全问题的出现。 

解决办法

最主要的方法就是:

就是本期的主角---锁。

简单来讲就是,通过加锁的这种方式,使一个线程在执行  count[0]++ 的操作时,其他线程的 count[0]++ 不能插队进来。如图~

看着是不是很像等待 join(),但效率是快多了~

​synchronized​ 关键字

这个关键字就代表上锁,基本用法是:

synchronized(//需要一个锁对象进行后续的判定){
//需要打包到一起的代码
}

重新加上这个关键字: 

public class demo17 {private static final Object lock = new Object(); //定义 lock 对象public static void main(String[] args) throws InterruptedException {int[] count = {0};Thread t1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {synchronized (lock) {count[0]++;}}});Thread t2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {synchronized (lock) {count[0]++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(count[0]);}
}

加上后,就能正常执行了~

注意

1.锁对象的作用:上面写的后续的判定就是指 是否是对 “同一个对象” 加锁.

记住,锁对象是什么不重要,重要的是多个线程的锁对象是否是同一个。

因为不是同一对象,各自加锁后也互不影响。也就不会出现阻塞或锁竞争。

比如下图~ 

public class demo17 {private static final Object lock1 = new Object(); //定义 lock1 对象private static final Object lock2 = new Object(); //定义 lock2 对象public static void main(String[] args) throws InterruptedException {int[] count1 = {0};int[] count2 = {0};Thread t1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {synchronized (lock1) {count1[0]++;}}});Thread t2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {synchronized (lock2) {count2[0]++;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(count1[0]);System.out.println(count2[0]);}
}

2.锁对象的类型不能是int double 这种内置的类型,只要是Object类的子类就可以~

看下图~ 

3.当 ​synchronized​ 关键字修饰方法时,这意味着当一个线程正在执行该方法时,其他线程必须等待,直到当前线程执行完毕。

看到最后,如果觉得文章写得还不错,希望可以给我点个小小的赞,您的支持是我更新的最大动力

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • SQL Server性能监控秘籍:数据库性能计数器阈值设置指南
  • 紫光展锐5G安卓核心板T760__国产手机芯片方案
  • 【分布式存储系统HDFS】架构和使用
  • Spring Boot集成starrocks快速入门Demo
  • laravel为Model设置全局作用域
  • Unity Apple Vision Pro 开发(四):体积相机 Volume Camera
  • golang性能调试工具net/http/pprof
  • 时序数据库如何选型?详细指标总结!
  • ubuntu如何彻底卸载android studio?
  • 掌握Python中的文件序列化:Json和Pickle模块解析
  • 笔记:Few-Shot Learning小样本分类问题 + 孪生网络 + 预训练与微调
  • Python面试整理-字典和集合的操作
  • Eureka——Spring Cloud中的服务注册与发现组件
  • python运维实战-ssh工具
  • 如何建设和维护数据仓库:深入指南
  • 2017-08-04 前端日报
  • Akka系列(七):Actor持久化之Akka persistence
  • C++11: atomic 头文件
  • css属性的继承、初识值、计算值、当前值、应用值
  • E-HPC支持多队列管理和自动伸缩
  • LeetCode18.四数之和 JavaScript
  • Netty+SpringBoot+FastDFS+Html5实现聊天App(六)
  • PHP面试之三:MySQL数据库
  • storm drpc实例
  • Vue.js-Day01
  • Web Storage相关
  • 程序员该如何有效的找工作?
  • 订阅Forge Viewer所有的事件
  • 仿天猫超市收藏抛物线动画工具库
  • 好的网址,关于.net 4.0 ,vs 2010
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 前端js -- this指向总结。
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 网络应用优化——时延与带宽
  • 一个完整Java Web项目背后的密码
  • 一加3T解锁OEM、刷入TWRP、第三方ROM以及ROOT
  • 异常机制详解
  • Spring Batch JSON 支持
  • 数据可视化之下发图实践
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​浅谈 Linux 中的 core dump 分析方法
  • #define用法
  • #ubuntu# #git# repository git config --global --add safe.directory
  • (2)STM32单片机上位机
  • (30)数组元素和与数字和的绝对差
  • (C#)获取字符编码的类
  • (rabbitmq的高级特性)消息可靠性
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (一)UDP基本编程步骤
  • (转)大型网站的系统架构
  • (转)拼包函数及网络封包的异常处理(含代码)
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)