【线程安全】如何保证一段代码的线程安全性?

前言

线程安全:

当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。

当多个线程访问某个方法时,不管你通过怎样的调用方式或者说这些线程如何交替的执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个是线程安全的。

如果一段代码可以保证多个线程访问的时候正确操作共享数据,那么它是线程安全的

线程不安全:

程序在多线程的执行环境下,程序的执行结果与与其结果不相符成为线程不安全。

导致线程不安全的原因

1.线程争抢,抢占式执行。

2.多个线程同时修改了同一个变量。(一对一修改、多对一读取、多对不同变量修改,是安全的)

3.操作非原子性操作。当 CPU 执行一个线程过程时,调度器可能调走CPU,去执行另一个线程,此线程的操作可能还没有结束;(通过锁来解决)

4.内存可见性问题。

5.指令重排序。Java的编译器在编译代码时,会针对指令进行优化,调整指令的先后顺序,保证原有逻辑不变的情况下,来提高程序的运行效率。

解决线程安全问题的方法

volatile解决指令重排序问题和内存可见性问题。

(1)volatile可以解决指令重排序问题和内存可见性问题,代码在写入volatile修饰变量的时候

改变线程工作内存的volatile变量副本的值
将改变后副本的值从工作内存刷新到主内

(2)代码在读取volatile修饰的变量的时候

从主内存中读取volatile变量最新值到线程的工作内存中
从工作内存中读取volatile变量的副本

使用锁解决线程安全问题

锁的特点:互斥的,同一时刻只有一个线程可以 获取到锁,其他线程如果尝试获取锁,就会发生阻塞等待,等到刚那个线程 释放锁 ,此时剩下的线程再重新竞争锁

基本操作:加锁,解锁(释放锁)

主要有两种锁:

  • 关键字synchronized
    1. 修饰静态方法:表示锁 this;
    2. 修饰普通方法:表示锁当前类的类对象(类对象就是 JVM运行时,将 .class 文件加载到内存中获取到的(类加载));对象名 . get Class()-->获取类对象
    3. 修饰代码块:加到某个代码之前,显示指定给某个对象加锁;

如果两个对象有两把锁,各自锁各自的,就不会涉及冲突 / 互斥

  • lock()

使用原子类来来解决操作原子性问题

线程安全性的三个体现

  • 原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作(Atomic、CAS算法、synchronized、Lock)
  • 可见性:一个主内存的线程如果进行了修改,可以及时被其他线程观察到(synchronized、volatile)
  • 有序性:如果两个线程不能从 happens-before原则 观察出来,那么就不能观察他们的有序性,虚拟机可以随意的对他们进行重排序,导致其观察观察结果杂乱无序(happens-before原则)
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容