JVM中(留神:马上讲到的这两个存储区只在JVM内部与物理存储区无关)存在一个主内存(Main Memory),Java中所有的变量存储在主内存中,所有实例和实例的字段都在此区域,对于所有的线程是共享的(相当于黑板,其他人都可以看到的)。每个线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中变量的拷贝,(相当于自己笔记本,只能自己看到),工作内存由缓存和堆栈组成,其中缓存保存的是主存中的变量的copy,堆栈保存的是线程局部变量。线程对所有变量的操作都是在工作内存中进行的,线程之间无法直接互相访问工作内存,变量的值得变化的传递需要主存来完成。在JMM中通过并发线程修改的变量值,必须通过线程变量同步到主存后,其他线程才能访问到。 
看看这个图是不是更形象

好啦,下面来看线程对某个变量的操作步骤: 
1.从主内存中复制数据到工作内存 
2.执行代码,对数据进行各种操作和计算 
3.把操作后的变量值重新写回主内存中

现在举个例子,设想两个棋手要通过两个终端显示器(Working Memory)对奕,而观众要通过服务器大屏幕(Main Memory )观看他们的比赛过程。这两个棋手相当于是同步中的线程,观众相当于其它线程。棋手是无法直接操作服务器的大屏幕的,他只能看到自己的终端显示器,只能先从服务器上将当前结果先复制到自己的终端上(步骤1),然后在自己的终端上操作(步骤2),将操作的结果记录在终端上,然后在某一时刻同步到服务器上(步骤3)。他所能看到的结果就是从服务器上复制到自己的终端上的内容,而要想把自己操作后的结果让其他人看到必须同步到服务器上才行。至于什么时候同步,那要看终端和服务器的通信机制。

回到这三个步骤,这个顺序是我们希望的,但是,JVM并不保证第1步和第3步会严格按照上述次序立即执行。因为根据java语言规范的规定,线程的工作内存和主存间的数据交换是松耦合的,什么时候需要刷新工作内存或者什么时候更新主存的内容,可以由具体的虚拟机实现自行决定。由于JVM可以对特征代码进行调优,也就改变了某些运行步骤的次序的颠倒,那么每次线程调用变量时是直接取自己的工作存储器中的值还是先从主存储器复制再取是没有保证的,任何一种情况都可能发生。同样的,线程改变变量的值之后,是否马上写回到主存储器上也是不可保证的,也许马上写,也许过一段时间再写。那么,在多线程的应用场景下就会出现问题了,多个线程同时访问同一个代码块,很有可能某个线程已经改变了某变量的值,当然现在的改变仅仅是局限于工作内存中的改变,此时JVM并不能保证将改变后的值立马写到主内存中去,也就意味着有可能其他线程不能立马得到改变后的值,依然在旧的变量上进行各种操作和运算,最终导致不可预料的结果。

这可如何是好呢?还好有synchronized和volatile: 
1.多个线程共有的字段应该用synchronized或volatile来保护. 
2.synchronized负责线程间的互斥.即同一时候只有一个线程可以执行synchronized中的代码
synchronized还有另外一个方面的作用:在线程进入synchronized块之前,会把工作存内存中的所有内容映射到主内存上,然后把工作内存清空再从主存储器上拷贝最新的值。而在线程退出synchronized块时,同样会把工作内存中的值映射到主内存,不过此时并不会清空工作内存。这样一来就可以强制其按照上面的顺序运行,以保证线程在执行完代码块后,工作内存中的值和主内存中的值是一致的,保证了数据的一致性! 
3.volatile负责线程中的变量与主存储区同步.但不负责每个线程之间的同步. 
volatile的含义是:线程在试图读取一个volatile变量时,会从主内存区中读取最新的值。现在很清楚了吧。

转自https://hellosure.iteye.com/blog/1121157

Java线程同步synchronized的理解的更多相关文章

  1. Java多线程-同步:synchronized 和线程通信:生产者消费者模式

    大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...

  2. java 线程同步 原理 sleep和wait区别

    java线程同步的原理java会为每个Object对象分配一个monitor, 当某个对象(实例)的同步方法(synchronized methods)被多个线程调用时,该对象的monitor将负责处 ...

  3. Java线程同步_1

    Java线程同步_1 synchronized 该同步机制的的核心是同步监视器,任何对象都可以作为同步监视器,代码执行结束,或者程序调用了同步监视器的wait方法会导致释放同步监视器 synchron ...

  4. java线程安全— synchronized和volatile

    java线程安全— synchronized和volatile package threadsafe; public class TranditionalThreadSynchronized { pu ...

  5. java线程 同步临界区:thinking in java4 21.3.5

    java线程 同步临界区:thinking in java4 21.3.5 thinking in java 4免费下载:http://download.csdn.net/detail/liangru ...

  6. Java线程同步的四种方式详解(建议收藏)

    ​ Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...

  7. Java线程同步之一--AQS

    Java线程同步之一--AQS 线程同步是指两个并发执行的线程在同一时间不同时执行某一部分的程序.同步问题在生活中也很常见,就比如在麦当劳点餐,假设只有一个服务员能够提供点餐服务.每个服务员在同一时刻 ...

  8. JAVA - 线程同步和线程调度的相关方法

    JAVA - 线程同步和线程调度的相关方法 wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁:wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等 ...

  9. Java线程(二):线程同步synchronized和volatile

    上篇通过一个简单的例子说明了线程安全与不安全,在例子中不安全的情况下输出的结果恰好是逐个递增的(其实是巧合,多运行几次,会产生不同的输出结果),为什么会产生这样的结果呢,因为建立的Count对象是线程 ...

随机推荐

  1. Java 基础:Queue

    下面几个关于Java里queue的说法哪些是正确的()? 正确答案: A C A.LinkedBlockingQueue是一个可选有界队列,不允许null值 B.PriorityQueue,Linke ...

  2. DM当中用文本输入点【转载】

    摘自<ANSYS 13.0 Workbench数值模拟技术> 通过XYZ坐标的文本文件创建3D曲线,文本需要满足一定的格式,格式化文本中,#表示此行是注释,忽略空行,数据行包括5个数据域, ...

  3. java本地与树莓派中采用UDP传输文本、图片

    今天解决了一个困扰好几天的问题,由于比赛需要,需要用java语言,并采用UDP传输协议,让树莓派与服务器(就是本机)建立连接传输视频,图片. 由于UDP是建立在无连接的协议上,因此就碰到了一个很尴尬的 ...

  4. arcgis python 获得arcgis的版本和安装路径

    import arcpy # Use the dictionary iteritems to iterate through # the key/value pairs from GetInstall ...

  5. spring boot修改代码后无需重启设置,在开发时实现热部署

    Spring Boot在开发时实现热部署(开发时修改文件保存后自动重启应用)(spring-boot-devtools) 热部署是什么 大家都知道在项目开发过程中,常常会改动页面数据或者修改数据结构, ...

  6. (二)SpringMVC+mybatis实践

    一.需求 以案例作为驱动 springmvc和mybatis使用一个案例(商品订单管理系统) 功能需求:商品的列表查询 二.环境准备 数据库环境 Java环境 jdk1.8 eclipse sprin ...

  7. phpstorm yii2框架的redis和mongodb提示

    随便找个目录创建一个 yii_helper.php 文件,内容如下: /** * Class Yii */ class Yii { /** * @var MyApplication */ public ...

  8. Java基础 Scanner 使用nextLine接收字符串

        JDK :OpenJDK-11      OS :CentOS 7.6.1810      IDE :Eclipse 2019‑03 typesetting :Markdown   code ...

  9. postgresql 查看用户名

    查看当前连接的用户名: foo=#select * from current_user; 或 foo=#select user; 查看所有用户名: foo=#\du 用户管理可以查看之前的博文: ht ...

  10. 【相机篇】从到FlyCapture2到Spinnaker

    从FlyCapture2 到 Spinnaker SDK的变换,可参见FLIR公司机器视觉的相机产品:https://www.flir.com/iis/machine-vision/ Spinnake ...