Java多线程之可见性分析

可见性:一个线程对共享变量值的修改,能够及时地被其他线程看到。


Java内存模型(JMM)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存中和从内存中读取出变量这样的底层细节。


多线程中所有的变量都存储在主内存中,每个线程都有自己的独立的工作内存,里面保存该线程使用的变量的副本(主内存中该变量的拷贝)。


两种方式实现多线程的可见性

synchronized实现可见性

synchronized在多线程中有两种主要的功能

  • 原子性(同步)
  • 可见性

JMM关于synchronized的两条规定:

  1. 线程解锁前,必须把共享变量的最新值刷新到主内存中
  2. 线程加锁时,将清空工作内存中共享的变量值,从而使用共享变量时需要从主内存中重新读取最新的值(PS:加锁与解锁需要的是同一把锁)。实现共享变量的可见性。


重排序:编译器或处理器为了提高程序性能而作的优化。

as-if-serial:无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致(Java编译器运行时和处理器都会保证Java在单线程下遵循as-if-serial语义)。


导致线程共享变量在线程中不可见的原因分析:

  1. 线程交叉执行 —->synchronized的原子性解决
  2. 重排序结合线程交叉执行 —->synchronized的原子性解决
  3. 共享变量更新后的值没有在工作内存与主内存间及时更新 —->synchronized的可见性解决

volatile实现可见性

ps:能够保证变量的可见性,不能保证变量符合操作的原子性。

volatile实现原理分析:

通过加入内存屏障和禁止重排序优化来实现的

  • 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令(强制刷入主内存)
  • 对volatile执行读操作时,会在读操作前加入一条load屏障(强制从主内存中更新到工作内存中)


eg:num++;包含三步操作,volatile无法保证原子性,多线程操作时,虽然保留了可见性,但无法保持同步。

解决方案:

  1. 使用synchronized关键字
  2. 使用ReentrantLock加锁操作(锁内部操作需要在try…finally中执行。finally中执行unlock()解锁操作)
  3. 使用AtomicInterger

要在多线程中安全使用volatile变量,必须满足

  1. 对变量的写入操作不依赖当前变量值
  2. 该变量没有包含在其他变量的不变式中

synchronized与volatile比较

  • volatile不需要加锁,更轻量级,不会阻塞线程
  • 从可见性角度分析:volatile读相当于加锁,volatile写相当于解锁
  • synchronized既能满足原子性又能满足可见性
时间: 2024-08-11 18:37:21

Java多线程之可见性分析的相关文章

(转)Java多线程的监控分析工具(VisualVM)

原文链接:http://blog.csdn.net/chendc201/article/details/22905511 在Java多线程程序运行时,多数情况下我们不知道到底发生了什么,只有出了错误的日志的时候,我们才知道原来代码中有死锁.撇开代码检查工具,我们先讨论一下利用VisualVM监控,分析我们的多线程的运行情况. AD:51CTO学院:IT精品课程在线看! 在Java多线程程序运行时,多数情况下我们不知道到底发生了什么,只有出了错误的日志的时候,我们才知道原来代码中有死锁.撇开代码检

java线程-java多线程之可见性

可见性:一个线程对共享变量值的修改,能够及时呗其他线程看到. 共享变量:如果一个变量在多个线程的内存中都存在副本,那么这个变量就是这几个线程的共享变量. java内存模型(JMM) 描述了java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和内存中读取出变量这样的底层细节. 1)所有的变量存储在主内存中 2)每个线程都有自己独立的工作内存,里面保存该线程中使用到的变量的副本(主内存中该变量的一份拷贝). 两条规定 1)线程对共享变量的所有操作都必须在自己的工作内存中进

Java多线程断点下载分析

1.主函数main()——调用抽象类DownloadUtils.java,传入参数:url.fileName.filePath.threadNum 2.DownloadUtils.java——是抽象类,download()为抽象方法,根据传入的参数选择调用的方法 2.1.创建DownloadInfo.java类的实例bean: DownloadInfo bean = new DownloadInof(url,fileName,filePath,threadNum); 2.2.通过LogUtils

Java多线程之可见性与原子性——synchronized VS volatile

程序举例: 代码: package com.synch; public class SynchronizedDemo { //共享变量 private boolean ready = false; private int result = 0; private int number = 1; //写操作 public synchronized void write(){ ready = true; number = 2; } //读操作 public synchronized void read

java多线程之内存可见性-synchronized、volatile

1.JMM:Java Memory Model(Java内存模型) 关于synchronized的两条规定: 1.线程解锁前,必须把共享变量的最新值刷新到主内存中 2.线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁和解锁需要是同一把锁) 注:线程解锁前对共享变量的修改在下次加锁时对其他线程可见 2.线程执行互斥代码的过程: 1.获得互斥锁 2.清空工作内存 3.从主内存拷贝变量的最新副本到工作内存 4.执行代码 5.将更改后的共享变量的值刷

java多线程与内存可见性

一.java多线程 JAVA多线程实现的三种方式: http://blog.csdn.net/aboy123/article/details/38307539 二.内存可见性 1.什么是JAVA 内存模型 共享变量 :如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量. Java Memory Model (JAVA 内存模型)描述线程之间如何通过内存(memory)来进行交互,描述了java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存

细说Java多线程之内存可见性

可见性:一个线程对共享变量值的修改,能够及时地被其他线程看到. 共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线 程的共享变量. Java内存模型: 线程对共享变量操作的规定: 共享变量可见性实现的原理: 可见性的必要条件: 可见性的实现方式:(final也可以) 线程执行互斥代码的过程: 重排序的概念: as-if-serial的概念: 导致共享变量在线程间不可见的原因: 可见性分析: 当一个线程访问object的一个sysnchronized(this)同步代

JAVA读书推荐----《深入分析Java Web技术内幕》--《java多线程编程核心技术》--《大型网站技术架构 核心原理与案例分析》-《Effective Java中文版》

(1)  首先推荐的不是一本书,而是一个博客,也是我们博客园另外一位博友java_my_life. 目前市面上讲解设计模式的书很多,虽然我前面讲了看书是最好的,但是对设计模式感兴趣的朋友们,我推荐的是这个博客.这位博友的设计模式讲得非常非常好,我认为90%的内容都是没有问题且很值得学习的,其讲解设计模式的大体路线是: 1.随便开篇点明该设计模式的定义 2.图文并茂讲解该设计模式中的结构 3.以详细的代码形式写一下该种设计模式的实现 4.补充内容 5.讲解该设计模式的优缺点 对于一个设计模式我们关

Java多线程总结之线程安全队列Queue

在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列.Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列. 注:什么叫线程安全?这个首先要明确.线程安全的类 ,指的是类内共享的全局变量的访问必须保证是不受多线程形式影响的.如果由于多线程的访问(比如修改.遍历.查看)而使这些变量结构被破坏