Java内存模型是什么?
          引用大师的一句话:“The Java Memory Model describes what behaviors are legal in multithreaded code, and how threads may interact through memory.”

翻译过来就是:Java内存模型描述了在多线程代码中哪些行为是合法的,以及线程如何通过内存进行交互。

所以其他涉及到多线程的编程语言都会涉及到内存模型,如C/C++等,并不是Java所特有的。

那为什么会出现内存模型呢?
         归结到底是因为CPU的频率无法再提高了,计算机开始面向多核化发展。为了充分利用多核带来的性能提升,便引入的并发编程,并发编程的难度是比较大的。

为了弥补cpu与缓存直接的速度落差,提供了多级高速缓存,使缓存更加接近cpu,处理速度更快。与此同时,就会出现缓存不一致问题,在多线程的状况下,每个cpu的计算结果会暂存在高速缓存中,而cpu1的缓存对于cpu2来说是不可见的,对于多个线程处理同一个共享数据时就会产生问题。如何解决?这里的问题根源就是可见性!

另外,cpu是一种很宝贵的资源,其内部运行采用了“流水线”的思想,比如一条指令的执行包含“取指、译码、执行、访问存储器、写回”等步骤,当一条指令进行译码的同时,cpu又可以继续取指操作。为了能更好地保证流水线的“完美”(尽量让cpu的每个步骤不闲着),在不影响单线程执行结果前提下,cpu会适当移动代码执行顺序(指令重排序),这样做cpu它觉得很完美,能够提高自身的执行效率,它不会管其他cpu怎么这么样。可是在多cpu协作的情况下,有时这是不允许的。如:

Class Reordering {
  int x = 0, y = 0;
  public void writer() {
    x = 1;
    y = 2;
  }
 
  public void reader() {
    int r1 = y;
    int r2 = x;
  }
}
       假入有两个线程,一个执行writer,一个执行reader,当reader读取到r1==2时,常理x读到的应该是1(因为x=1在y=2前面)。但实际是不一定的,cpu可能对xy的赋值语句调换了顺序,导致x可能读到0。这个的问题根源就是有序性!

针对这两个问题,让我这种码农来处理显然是不现实的,所以大师们在计算机指令的层面已经为我们实现好了,并提供相应的语法,并发类供我们使用。解决这些问题的过程中,大师们设定了一些规则,避免了上述错误情况的发生,这就是JAVA内存模型。

参考:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalWrong

Java内存模型中有哪些规则?
       其中最重要的就是happens-before原则,我认为这个原则表达的是“哪些情况下后面的代码对前面的代码是可见的”,接下来让我们看看有哪些情况:

引入大师的说法:

Each action in a thread happens before every action in that thread that comes later in the program's order.
An unlock on a monitor happens before every subsequent lock on that same monitor.
A write to a volatile field happens before every subsequent read of that same volatile.
A call to start() on a thread happens before any actions in the started thread.
All actions in a thread happen before any other thread successfully returns from a join() on that thread.
翻译一下:

1、单线程中,前面的代码应该happens before于后面的代码。(这个很好理解,先来后到)

2、对同一个监视器(锁)的解锁应该happens before于后面的加锁。(一个监视器只能同时被一个线程持有,前一个线程解锁,后面的线程才能加锁,这也是synchronized遵守的规则之一)

3、volatile字段的写入应该happens before于后面对同一个volatile字段的读取。

4、主线程中启动子线程,子线程能看到启动前主线程中的所有操作。

5、主线程中启动子线程,然后子线程调用join方法,主线程等待子线程执行结束,执行结束返回后,主线程对看到子线程的所有操作。

另外还提供了synchronized、volatile、final三个关键字来解决可见性、有序性问题。

例子1:
单核cpu仍然存在线程安全问题,因为如果操作不是原子操作,你无法控制cpu在什么时机切换线程,我采用了阿里云上的一台单核服务器做实验,以下是实验代码:

public class OneCpuCoreTest implements Runnable {
 
    private static int count;
 
    @Override
    public void run() {
        int idx = 0;
        while (idx++ < 100000) {
            count += 1;
        }
 
    }
 
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new OneCpuCoreTest());
        Thread thread2 = new Thread(new OneCpuCoreTest());
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println(count);
    }
}
我们知道,自增操作是不具备原子性的,它包含取数据、+1、数据写回操作,所以这里的count应该是小于200000,count是线程不安全的。

PC机上(多核):

而在阿里云(单核cpu)上:

看似在单核cpu上是没有线程安全问题。但错了,这里是因为在一个cpu时间片内执行完了,所以不明显,当把循环次数调大。

结果就不一样了。

所以单核cpu上多线程仍然会存在线程安全问题,因为单核cpu仍然存在线程切换,在执行非原子操作的时候,仍然存在线程问题。
————————————————
版权声明:本文为CSDN博主「c&amp;0xff00」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_37968613/article/details/105510239

单核CPU仍然存在线程安全问题相关推荐

  1. 单核CPU是否有线程可见性问题?

    本文仅是本人对问题的思考记录,并没有实操验证,有误请大家评论指出. 今天见到了一个经典的问题,单核CPU是否有线程可见性问题,学完操作系统应该可以直接回答,不会有线程安全问题.但如果结合JVM虚拟机来 ...

  2. 线程安全问题的本质详解: 原子性、有序性、可见性

    内容导航 volatile的作用 什么是可见性 volatile源码分析 一.volatile的作用 在多线程中,volatile和synchronized都起到非常重要的作用,synchronize ...

  3. 多线程的创建方式和解决线程安全问题

    一  基本概念 ①程序(program)是为完成特定任务.用某种语言编写的一组指令的集合.即指一 段静态的代码,静态对象. ②进程(process)是程序的一次执行过程,或是正在运行的一个程序.是一个 ...

  4. hash是线程安全的吗?怎么解决?_这次进程、线程、多线程和线程安全问题,一次性帮你全解决了...

    1. 什么是进程 一个软件,在操作系统中运行时,我们称其为进程. 进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元. 2. 什么是线程 在一个进程中,每个独立的功能都需要独立的去运行,这 ...

  5. iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用(上)

    2017-07-08 remember17 Cocoa开发者社区 目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 ...

  6. 12.多线程的实现方式、线程安全问题的产生与解决以及生产者与消费者问题

    一.实现多线程 1.1 了解多线程 多线程是指从软件或者硬件上实现多个线程并发执行的技术,具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,从而提升性能. 1.2 并发与并行 并行是指同 ...

  7. 面试:从volatile说到i++的线程安全问题

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者|ZimZz 来源|https://www.cnbl ...

  8. 多线程别怕,有锁就安全;(解决线程安全问题)

    多线程de小事情 导航不迷路: 程序.进程以及线程的爱恨情仇 最简单实现多线程的方法(Thread) 简单易懂的多线程(通过实现Runnable接口实现多线程) 常用获取线程基本信息的方法(新手专属) ...

  9. 多线程,你觉得你安全了?(线程安全问题)

    多线程de小事情 导航不迷路: 程序.进程以及线程的爱恨情仇 最简单实现多线程的方法(Thread) 简单易懂的多线程(通过实现Runnable接口实现多线程) 常用获取线程基本信息的方法(新手专属) ...

最新文章

  1. java 定义类变量初始化吗_Java的变量有哪些类型?变量如何定义?如何初始化?请说明理由并举例_学小易找答案...
  2. 穿迷宫、搭积木、现场编程 看看孩子们的机器人“大战”
  3. 从OpenStack Newton发布看开源云计算
  4. C++ int转string
  5. Binary Tree Non-recursive Traversal
  6. 计算机编程工程师理论知识,结构工程师基础知识点:程序设计语言
  7. Angular里的structural directive的一个例子
  8. 【vue-router①】router-link跳转页面传递参数 - 进击的前端之路(偶尔爬坑java小路) - SegmentFault 思否
  9. Anaconda简介以及安装
  10. 关于mysql的一些问题
  11. 爬虫用java还是python_网络爬虫是用python比较好,还是Java比较好呢?
  12. 开源!让图像识别训练速度提升了8.7倍
  13. 关闭Xcode警告 (强迫症还在等什么!)
  14. java bitmap 排序_Java实现2-BitMap排序
  15. overfitting怎么解决?
  16. linux安装包安装nginx,Linux tar包安装Nginx
  17. APK反编译JAVA源码
  18. 蓝牙 - BQB认证测试
  19. 计算机大作业的范本,计算机应用基础大作业.doc
  20. 最常用激活函数公式(更新中)

热门文章

  1. ODOO13 开发教程三 开始你的第一个模块
  2. wps的计算机在哪里设置密码,wps_WPS Office如何设置密码?_office设置密码
  3. UA MATH563 概率论的数学基础1 概率空间4 实数域上的概率测度
  4. Unreal4 入门
  5. 电子技术基础(三)__电感的感抗_无功功率和电容的容抗_无功功率
  6. UE4 无需切线空间应用凹凸贴图
  7. matlab定步长ode,[转载]matlab ode45 函数传自定义参数用法及定步长ode
  8. Windows下的MySQL实例没有mysql.user表#Olivia丶长歌#
  9. Python道路之——画自己名字
  10. eureka集群只注册一个_闲聊注册中心——ZK、Eureka、Sofa-Registry