吃透Java线程安全问题(下)

简介: 吃透Java线程安全问题(下)

🍑优化过程中所造成的线程不安全

上述三个是造成线程安全问题的的主要原因,除此之外。在编译器/JVM/操作系统对程序优化的过程中也会好心办坏事,造成线程不安全

1、内存可见性引起的安全问题


38b0b7352ae140708ba8f832fee4632f.png分析

92d8ec09f4b1487b8c387b7cbc6743e0.png


🌰栗子

219a457d35af44cfaf59912e50ab28bd.png

为什么会这种情况呢?

但我们的t1、t2创建好了后,操作系统内存对我们这两个线程进行随机调度执行。那么我们的t1线程在执行过程中就不断地从内存中读取flag的值,然后进行判断。那么就有可能t1线程在执行了好长一会后,t2才执行、输入了一个整数&&改变了flag值。


是操于作系统就发现,怎么回事,这个flag值一直也没有改变,那么t1线程一直不断地从内存中读取flag的值,这样的效率是很低的。于是操作系统就对这种情况进行了优化,t1在第一次从内存中读取flag的值后就不再反复的从内存中读取了,接下来只是不断地进行判断——也就是说:在t2线程把flag的值更改了后,t1线程并没有成功读取到更改后的flag值。


解决方案

8d12571d064d465ea71d0932e08d5164.png

9901ceb190c44a99998d30d438def979.png

再补充一个栗子

7422692d671e4685ba90a490f29290ab.png

6dc763522eb54fcab6eb83148f5b7285.png7abc5933198540928fb6a1d278876601.png

6dc763522eb54fcab6eb83148f5b7285.png

比如说接下来我要干三件事

1、去超市闲逛

2、 回家

3、去超市买菜


如果我安装1->2->3的顺序来执行,是不是很傻、很浪费时间。在程序执行的过程中也是如此,通过顺序的改变和调整就可以达到优化的效果。

但有时候顺序是不能随便改变的


21dbc2d096d444b2acaf45abc36309ec.png

编译器 / JVM / 操作系统 优化优化着,还优化出BUG了,那为啥还要有优化呢?


因为不同的程序员的水平差异是非常大的,通过优化,对我们的程序执行效率可能会有翻倍的提升!!!服务器启动的时候,如果加上编译器优化,启动时间10min!但如果关闭优化,启动时间 > 30 min!!!


所以说,不管怎么优化,我们的大前提是要保证程序的逻辑是不变的!我们是希望在逻辑不变的前提下,通过一些优化的操作来提升效率、提高速度!!!


补充一下在单线程下,保证逻辑不变很容易做到。但在多线程环境下,想有在不改变逻辑的前提下优化就变得很困难了——所以才会出现内存可见性问题、指令重排序问题


三、总结

7d0ad44f06884f148d1df73bde4ecedb.png

集合类安全性的一点补充:

线性安全的集合类

Vector:只要是关键性的操作,方法前面都加了synchronized关键字,来保证线程的安全性

Hashtable:使用了synchronized关键字,所以相较于Hashmap是线程安全的。

ConcurrentHashMap:使用锁分段技术确保线性安全,是一种高效但是线程安全的集合。

Stack:栈,也是线程安全的,继承于Vector。

线性不安全的集合类

  • Hashmap
  • Arraylist
  • LinkedList
  • HashSet
  • TreeSet
  • TreeMap


Hashmap:HashMap在put操作的时候,如果插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是resize,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。


Arraylist: List 对象在做 add 时,执行 Arrays.copyOf 的时候,返回一个新的数组对象。当有线程 A、B… 同时进入 grow方法,多个线程都会执行 Arrays.copyOf 方法,返回多个不同的 elementData 对象,假如,A先返回,B 后返回,那么 List.elementData ==A. elementData,如果同时B也返回,那么 List.elementData ==B. elementData,所以线程B就把线程A的数据给覆盖了,导致线程A的数据被丢失。


LinkedList:与Arraylist线程安全问题相似,线程安全问题是由多个线程同时写或同时读写同一个资源造成的。


HashSet:底层数据存储结构采用了Hashmap,所以Hashmap会产生的线程安全问题HashSet也会产生。


SimpleDateFormat: 对象是线程不安全的

相关文章
|
4月前
|
存储 安全 Java
【Java集合类面试二十五】、有哪些线程安全的List?
线程安全的List包括Vector、Collections.SynchronizedList和CopyOnWriteArrayList,其中CopyOnWriteArrayList通过复制底层数组实现写操作,提供了最优的线程安全性能。
|
4月前
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
Java并发编程实战:使用synchronized关键字实现线程安全
64 0
|
1月前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
1月前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
51 4
|
18天前
|
SQL 安全 Java
Java 异常处理:筑牢程序稳定性的 “安全网”
本文深入探讨Java异常处理,涵盖异常的基础分类、处理机制及最佳实践。从`Error`与`Exception`的区分,到`try-catch-finally`和`throws`的运用,再到自定义异常的设计,全面解析如何有效管理程序中的异常情况,提升代码的健壮性和可维护性。通过实例代码,帮助开发者掌握异常处理技巧,确保程序稳定运行。
32 0
|
2月前
|
存储 安全 Java
Java-如何保证线程安全?
【10月更文挑战第10天】
|
2月前
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
|
3月前
|
安全 Java API
java安全特性
java安全特性
29 8
|
3月前
|
安全 Java 调度
Java 并发编程中的线程安全和性能优化
本文将深入探讨Java并发编程中的关键概念,包括线程安全、同步机制以及性能优化。我们将从基础入手,逐步解析高级技术,并通过实例展示如何在实际开发中应用这些知识。阅读完本文后,读者将对如何在多线程环境中编写高效且安全的Java代码有一个全面的了解。
|
3月前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
72 11