Java 多线程3——三种线程不安全的BUG+解决方案


前言

本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下!

本篇文章主要讲解了线程不安全的场景,以及如何解决线程不安全问题,内容可能有点抽象,希望大家可以慢慢咀嚼,好好吸收。


一、线程不安全场景

何为线程安全

  • 如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线程安全的

导致线程不安全的原因,主要是有三个场景。

1️⃣ 多个线程对同一个共享数据进行修改操作
2️⃣ 内存可见性(是编译器出现了误判后,对代码做出的错误优化)
3️⃣ 指令重排序(是编译器出现了误判后,对代码做出的错误优化)

编译器优化
编译器优化:智能的调整你的代码执行逻辑,保证程序结果不变的前提下,通过加减语句,通过语句执行顺序变换,通过一些操作,让整个程序执行的效率大大提升。

编译器对于"程序结果不变"

  • 单线程下判定是非常准确的。
  • 多线程下判定就不一定了,可能调整后,效率提高了,但是结果改变了(编译器出现误判),引起程序出现bug。

1. 多个线程修改同一共享数据

public class ThreadDemo2 {
   
    private static int i = 0;//全局变量
    public static void main(String[] args) throws InterruptedException {
   
        // 创建一个线程让i++ 5000次
        Thread thread1 = new Thread(() -> {
   
            for(int j = 0;j < 5000;j++) {
   
                i++;
            }
        });
        // 再创建一个线程让i++ 5000次
        Thread thread2 = new Thread(() -> {
   
            for(int j = 0;j < 5000;j++) {
   
                i++;
            }
        });
        thread1.start();
        thread2.start();
        // 两个线程开始执行
        thread1.join();
        thread2.join();
        // 等待两个线程执行完毕打印i
        System.out.println(i);
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上述代码,运行了三次,三次结果都不一样且都不符合预期结果的10000。

那么为什么会造成以上结果呢??


造成以上线程不安全问题主要有三个原因
1️⃣ 线程之间是抢占式执行的,CPU执行到任意一条语句都可能被调度去执行其他线程 (罪魁祸首,主要原因)
2️⃣ 多个线程修改同一个共享变量
3️⃣ 修改操作不是原子性的

前两条是代码的执行机制,而这两个机制,遇上第三个原因,就会出现大问题。


什么是原子性?

在以前,人们还没有发现中子,质子,电子时,人们认为不可分割的最小物质就是原子。

因此,我在执行一个操作时,

如果这个操作不可以被分割成几个步骤,必须一次性执行完毕,那么这个操作具备原子性。

如果这个操作可以被分割成几个步骤,可以通过CPU调度来间断性的完成这个操作,那么这个操作不具备原子性。

某个操作对应单个CPU指令,那么这个操作就是原子性的
某个操作对应多个CPU指令,那么这个操作大概率就不是原子性的

使用 ‘=’ 赋值,就是一个原子性操作。
而i++对应了三个CPU指令。


就比如上述的i++操作,是由三步操作组成的。

1️⃣load(从内存把数据读到CPU)
2️⃣add(CPU对数据进行运算)
3️⃣save(把数据写回内存)

在这里插入图片描述

此时已经执行了两次i++,而 i 仍等于1。

此处i++这个操作是由三个CPU指令来完成的,因此两个线程,抢占式执行,就可能存在多种指令顺序排列,因此造成bug。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值