并发编程出问题的原因

一、计算机为提升性能做出的改变

1.1 CPU增加了缓存

CPU对于数据的计算速度远远高于从内存中存取数据的速度,为了缓和CPU与内存之间的速度差异,计算机的制造商为CPU增加了缓存

1.2操作系统增加了进程和线程

为了缓和CPU和磁盘设备之间的速度差异,操作系统的制造商增加了进程和线程技术。

1.3优化CPU指令执行顺序

为了使CPU的缓存能够得到更加合理的利用,编译程序对CPU上指令的执行顺序进行了优化。

二、计算机改变所引起的并发编程问题

2.1 缓存导致的可见性问题

说的直白些,就是两个线程共享一个变量,无论哪一个线程修改了这个变量,则另外的一个线程都能够看到上一个线程对这个变量的修改。这里的共享变量,指的是多个线程都能够访问和修改这个变量的值,那么,这个变量就是共享变量。

可见性就是说一个线程对共享变量的修改,另一个线程能够立刻看到。

在CPU单核时代,操作系统上所有的线程都是运行在同一个CPU上,操作同一个CPU的缓存。一个线程对缓存的写,对另外一个线程一定可见,也就是说,在单核CPU中,不存在线程的可见性问题。

在多核CPU中,每个CPU内核都有自己的缓存。当多个线程在不同的CPU核心上运行时,这些线程操作的是不同的CPU缓存。一个线程对缓存的写,对另外一个线程不一定可见。

public class TestThread {
    private long count = 0;
    //对count的值累加10000次
    private void addCount() {
        for (int i = 0; i < 10000; i++) {
            count++;
        }
    }
    public long execute() throws InterruptedException {
//创建两个线程,执行count的累加操作
        Thread threadA = new Thread(() -> {
            addCount();
        });
        Thread threadB = new Thread(() -> {
            addCount();
        });
//启动线程
        threadA.start();
        threadB.start();
//等待线程执行结束
        threadA.join();
        threadB.join();
//返回结果
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        TestThread testThread = new TestThread();
        long count = testThread.execute();
        System.out.println(count);
    }
}

运行三次结果分别为:11371,11665,12379

变量count属于ThreadTest类的成员变量,这个成员变量对于线程A和线程B来说,是一个共享变量。假设线程A和线程B同时

执行,它们同时将count=0读取到各自的工作内存中,每个线程第一次执行完count++操作后,同时将count的值写入内存,此时,

内存中count的值为1,而不是我们想象的2。而在整个计算的过程中,线程A和线程B都是基于各自工作内存中的count值进行计算。

这就导致了最终的count值小于2000

2.2 线程切换带来的原子性问题

原子性是指一个或者多个操作在CPU中执行的过程不被中断的特性。

图中存在两个线程,分别为线程A和线程B,其中线程A和线程B中的每个小方块代表此时线程占有CPU并执行任务,每个虚线部分代表此时的线程不占用CPU资源。CPU会在线程A和线程B之间频繁切换。

Java并发程序是基于多线程来编写的,这也会涉及到CPU对任务的切换。

看似简单的一条count自增的代码,实际上对应着CPU中的多条指令。我们将CPU的指令简化成如下三步操作。

指令1:把变量count从内存加载的CPU寄存器。

指令2:在寄存器中执行count++操作。

指令3:将结果写入缓存(可能是CPU缓存,也可能是内存)。

在操作系统执行线程切换时,可能发生在任何一条CPU指令完成后,而不是程序中的某条语句完成后。如果线程A执行完指令1后,操作系统发生了线程切换,当两个线程都执行count++操作后,得到的结果是1而不是2。

个人理解最终count=2;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值