Object obj = new Object()引出的线程安全思考

背景

上次在部门周会上抛出了一段代码:

1.Class SimpleCache {
2.   private Map cache = new HashMap() ;
3.   public Object get(String key) {
4.      return cache.get(key);
5.   }
6.
7.   public void reload(){
8.      Map tempCache = loadFromDB();
9.      cache = tempCache; // 位置1,引用切换
10.   }
11.}

是否是一个线程安全问题的操作。看似很简单的问题,其实发现自己也很难理的清楚,自己也是"道听涂说"的抛出了问题。

这里的关键点是在对应的位置1上,多线程中进行了一个引用切换,这是否是一个线程安全的操作??

 

因为jvm中的引用是基于双字节进行存储,会不会出现写了高位后,线程被换出,另一个线程读到了一个破损的地址导致程序出现异常?

过程

也没有绝对的标准,自己也试着去尝试分析一下这个问题。

 

首先看一下,jvm的内存模型:   http://kenwublog.com/explain-java-memory-model-in-detail

比较认可里面提到的几个内存模型的特征: 

 

  • Visibility 可视性
  • Ordering 有序性

这里也给出了对应jmm规范中,对应的工作模式: 

 

里面有对应的 main memory(主存)和work memory(工作内存)两类。

 

思考一下,java奉行的是"一处编译,到处运行“的理念,其实java是自己起了一个"操作系统",定义了自己的jmm模型,包括一些资源请求,多线程调度等。

所以jmm的定义,宏观上来说就是可以类比于操作系统的一些概念:

 

  • main memory  <=>  操作系统中的内存概念
  • work memory  <=>  cpu cache(L1,L2高速cache)

可以细细体会一下,是不是有点这么个意思?

继续往下看:

1. jmm中Ordering 有序性

操作系统中的多进程的资源进程,主要由mutex机制,P/V原语, semaphore信号量。

P,V原语:http://baike.baidu.com/view/809762.htm

1.P(mutex); // 减少资源数
2.V(mutex); // 增加资源数

映射到jvm来看,其实都知道synchronized关键字,最终编译为class字节码后:

1.monitorenter
2.// do xxx
3.monitorexit

2. jmm中的Visibility 可视性

操作系统的cpu现在基本都已经步入多cpu,多核的时代,同时为了提升cpu的处理效率,引入了多级cache,比如L1,L2,L3。

用过cache的人都知道数据的一致性问题一直是一个头疼的问题,同样这个在cpu cache中同样的存在。 

看一下几篇文章:

linux下查看cpu cache : 

1.$ more /var/log/dmesg |grep cache
2.CPU: L1 I cache: 32K, L1 D cache: 32K
3.CPU: L2 cache: 256K
4.CPU: L3 cache: 8192K

解决缓存一致性的一般方法思路: 

a, 顺序一致性模型:

   要求某处理器对所改变的变量值立即进行传播, 并确保该值被所有处理器接受后, 才能继续执行其他指令.

b, 释放一致性模型:

   允许处理器将改变的变量值延迟到释放锁时才进行传播.

映射为jvm的多线程的work memory,所以同样会存在类似的问题,所以引入volatile关键字,可以用于解决数据可视性的问题。 

这里会引出jmm缓存一致性模型 – “happens-before ordering(先行发生排序)”,可以看一下: http://www.iteye.com/topic/260515

其他的一些内容

1. 指令重排:

这里有篇不错的文章,介绍的比较简单明了: http://kenwublog.com/illustrate-memory-reordering-in-cpu

为什么需要指令重排,就是cpu在一定的原则下,尽量加速cpu的执行速度,L1和L2cache的拓扑结构会决定其相关的一些行为。

2. 内存栅栏

正是因为有了指令重排的策略的存在,所以针对多线程编程,需要有一种barrier的策略。  http://www.infoq.com/articles/memory_barriers_jvm_concurrency

针对jmm定义中的volatile和synchronized做一些barrier指令的处理。就如那infoq文章中的描述: 

 

volatile在x86生成的指令:

1.0x03f83448: mfence                    ;...0faef0  

synchronized生成的指令:

1.10  0x04d5edc0: lock cmpxchg %edi,(%esi)  ;...f00fb13e
2....
3.18  0x04d5ede5: inc    %esi               ;...46
4....
5.25  0x04d5edfd: lock cmpxchg %esi,(%edi)  ;...f00fb137

atomic compareAndSet()方法生成的指令:

1.14  0x02445220: lock cmpxchg %esi,(%edi)  ;...f00fb137  

分析 

了解了前面的一些知识背景后,再回过来看一下最初的那段代码,是否存在线程安全问题?

答: 

  严格意义上来说这段代码会存在一些问题,因为没法保证cache的一致性问题,简单点的处理是增加一个volitile声明变量,保证线程更新时能更新cache指令。 (happens-before原则)

  java针对多字节的操作,java规范中有段描述: Java language Specification

1.When a thread uses the value of a variable, the value it obtains is in fact a value stored into the variable by that thread or by some other thread.
2.This is true even if the program does not contain code for proper synchronization.
3.For example, if two threads store references to different objects into the same reference value, the variable will subsequently contain a reference to one object or the other,
4.not a reference to some other object or a corrupted reference value. (There is a special exception for long and double values; see §17.4.  
1.17.4 Nonatomic Treatment of double and long
2.
3.If a double or long variable is not declared volatile, then for the purposes of load, store, read, and write actions they are treated as if they were two variables of 32 bits each:
4.wherever the rules require one of these actions, two such actions are performed, one for each 32-bit half.
5.The manner in which the 64 bits of a double or long variable are encoded into two 32-bit quantities is implementation-dependent.
6.The load, store, read, and write actions on volatile variables are atomic, even if the type of the variable is double or long.  

 大意主要是说:java中除了long,double两种变量外,其他的变量类型针对多线程的赋值操作,不会出现写入一个字节的破损情况。  

 

stackoverflow.com的一个类似问题:http://stackoverflow.com/questions/1351223/thread-safe-setting-of-a-variable-java

 

感触

自己得多看看jls :  Java language Specification,可以加深自己对多线程的理解,同时也可以找到一些最权威的解惑,不会被他人的一些转载啥的文章给误导了

不过自己的e文能力需加强下,看的速度比较慢。对不起了老师,大学后基本没好好学英文

时间: 2024-10-03 13:24:47

Object obj = new Object()引出的线程安全思考的相关文章

线程-static Object obj = new Object(),静态对象和非静态对象有什么区别

问题描述 static Object obj = new Object(),静态对象和非静态对象有什么区别 下面你这段程序中,Object不用static 修饰时不会产生死锁,加上static就会产生死锁,为什么会这样呢? public class TestDeadLock implements Runnable{ static Object o1 = new Object(); //在Object前面加static与不加对synchronized有什么影响,为什么执行结果不一样 static

多线程 同步 信号量-如果synchronized(new Object){} 和 Object obj = new Object(); synchronized(obj){}的区别是什么?

问题描述 如果synchronized(new Object){} 和 Object obj = new Object(); synchronized(obj){}的区别是什么? 如果synchronized(new Object){} 和 Object obj = new Object(); synchronized(obj){}的区别是什么? 主要是在synchronized()中放new Object 是四个线程一起,而放obj对象是一个一个的执行,这是什么原因呢? 代码: class X

java-for(Object obj : args)

问题描述 for(Object obj : args) 类似 for(Object obj : args) 这种的是什么意思?? : 是什么意思?? 解决方案 oreach语句是java5的新特征之一,在遍历数组.集合方面,foreach为开发人员提供了极大的方便. foreach语句是for语句的特殊简化版本,但是foreach语句并不能完全取代for语句,然而,任何的foreach语句都可以改写为for语句版本. foreach并不是一个关键字,习惯上将这种特殊的for语句格式称之为"for

compareto-Java 分数比较compareTo(Object obj)方法

问题描述 Java 分数比较compareTo(Object obj)方法 要求:1. 不能用分子除与分母的结果来比较大小,这样不精确.2.分母为零分子为正数时分数为无穷大,两个无穷大的数相等,无穷大的数大于一切其他数:分母为零分子为负数时分数为负无穷大,两个负无穷大的数相等,负无穷小于一切其他数.3. 正无穷大大于负无穷大,两个负无穷大的分数一样大,两个正无穷大的分数一样大.谢谢指导. 解决方案 假设你的分数类class fraction implements Comparable{publi

Object.wait()与Object.notify()的用法详细解析_java

wait.notify和notifyAll方法是Object类的final native方法.所以这些方法不能被子类重写,Object类是所有类的超类,因此在程序中有以下三种形式调用wait等方法. 复制代码 代码如下: wait();//方式1:this.wait();//方式2:super.wait();//方式3 void notifyAll()解除所有那些在该对象上调用wait方法的线程的阻塞状态.该方法只能在同步方法或同步块内部调用.如果当前线程不是锁的持有者,该方法抛出一个Illeg

微信小程序把玩(三十一)wx.uploadFile(object), wx.downloadFile(object) API

原文:微信小程序把玩(三十一)wx.uploadFile(object), wx.downloadFile(object) API 反正我是没有测通这两个API!!!!不知道用的方式不对还是其他的!!!先记录下回头再说... 主要方法: wx.uploadFile(OBJECT)上传 wx.downloadFile(OBJECT)下载 wxml <button type="primary" bindtap="listenerButtonDownLoadFile"

uft12 object spy-UFT 12 Object Spy 是灰色的不能用

问题描述 UFT 12 Object Spy 是灰色的不能用 Hi, 我安装的UFT12 的object spy 按钮为什么是灰色的,有人知道吗? Thanks.

java-List&amp;amp;lt;实体&amp;amp;gt;怎么转换成List&amp;amp;lt;Object[]&amp;amp;gt;

问题描述 List<实体>怎么转换成List<Object[]> 我用Mybaits写的SQL返回的是一个List<实体>,可是怎么能把这个实体转换成List类型啊 解决方案 说实在的,没太看明白你的意图,有下面两个方案,仅供参考. public class TestMain1 { public static void main(String[] args) { List<Sample> list = new ArrayList<Sample>(

java线程阻塞中断和LockSupport的常见问题

上周五和周末,工作忙里偷闲,在看java cocurrent中也顺便再温故了一下Thread.interrupt和java 5之后的LockSupport的实现.   在介绍之前,先抛几个问题.   Thread.interrupt()方法和InterruptedException异常的关系?是由interrupt触发产生了InterruptedException异常? Thread.interrupt()会中断线程什么状态的工作? RUNNING or BLOCKING? 一般Thread编程