- 浏览: 1595561 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
jsrgzhangzhiyong:
关于null值的转换还是感觉不太友好,就像 mapstruct ...
我也造了个轮子:BeanMapping(属性拷贝) -
he037:
a417930422 写道引用使用EPHEMERAL会引出一个 ...
基于zookeeper的分布式lock实现 -
seancheer:
qianshangding 写道首先节点启动后,尝试读取本地的 ...
zookeeper学习记录三(session,watcher,persit机制) -
雪夜归人:
您好,我想咨询一下,开源的canal都能支持mysql的哪些版 ...
Canal BinlogChange(mysql5.6) -
zhoudengyun:
copy 一份做记录,后续学习,请知悉
阿里巴巴开源项目: 基于mysql数据库binlog的增量订阅&消费
背景
上次在部门周会上抛出了一段代码:
Class SimpleCache { private Map cache = new HashMap() ; public Object get(String key) { return cache.get(key); } public void reload(){ Map tempCache = loadFromDB(); cache = tempCache; // 位置1,引用切换 } }
是否是一个线程安全问题的操作。看似很简单的问题,其实发现自己也很难理的清楚,自己也是"道听涂说"的抛出了问题。
这里的关键点是在对应的位置1上,多线程中进行了一个引用切换,这是否是一个线程安全的操作??
因为jvm中的引用是基于双字节进行存储,会不会出现写了高位后,线程被换出,另一个线程读到了一个破损的地址导致程序出现异常?
过程
也没有绝对的标准,自己也试着去尝试分析一下这个问题。
首先看一下,jvm的内存模型: http://kenwublog.com/explain-java-memory-model-in-detail
比较认可里面提到的几个内存模型的特征:
- Visibility 可视性
- Ordering 有序性
里面有对应的 main memory(主存)和work memory(工作内存)两类。
思考一下,java奉行的是"一处编译,到处运行“的理念,其实java是自己起了一个"操作系统",定义了自己的jmm模型,包括一些资源请求,多线程调度等。
所以jmm的定义,宏观上来说就是可以类比于操作系统的一些概念:
- main memory <=> 操作系统中的内存概念
- work memory <=> cpu cache(L1,L2高速cache)
P(mutex); // 减少资源数 V(mutex); // 增加资源数
monitorenter // do xxx monitorexit
- http://baike.baidu.com/view/206014.html cpu cache文库
- http://wenku.baidu.com/view/664a3f6fb84ae45c3b358c5b.html 多核处理器技术
- http://blog.csdn.net/zhuliting/archive/2011/02/27/6210921.aspx 多处理器系统MESI cache一致性协议
$ more /var/log/dmesg |grep cache CPU: L1 I cache: 32K, L1 D cache: 32K CPU: L2 cache: 256K CPU: L3 cache: 8192K
2. 内存栅栏
正是因为有了指令重排的策略的存在,所以针对多线程编程,需要有一种barrier的策略。 http://www.infoq.com/articles/memory_barriers_jvm_concurrency
针对jmm定义中的volatile和synchronized做一些barrier指令的处理。就如那infoq文章中的描述:
volatile在x86生成的指令:
0x03f83448: mfence ;...0faef0
synchronized生成的指令:
10 0x04d5edc0: lock cmpxchg %edi,(%esi) ;...f00fb13e ... 18 0x04d5ede5: inc %esi ;...46 ... 25 0x04d5edfd: lock cmpxchg %esi,(%edi) ;...f00fb137
atomic compareAndSet()方法生成的指令:
14 0x02445220: lock cmpxchg %esi,(%edi) ;...f00fb137
分析
了解了前面的一些知识背景后,再回过来看一下最初的那段代码,是否存在线程安全问题?
答:
严格意义上来说这段代码会存在一些问题,因为没法保证cache的一致性问题,简单点的处理是增加一个volitile声明变量,保证线程更新时能更新cache指令。 (happens-before原则)
java针对多字节的操作,java规范中有段描述: Java language Specification
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. This is true even if the program does not contain code for proper synchronization. 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, 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.
17.4 Nonatomic Treatment of double and long 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: wherever the rules require one of these actions, two such actions are performed, one for each 32-bit half. The manner in which the 64 bits of a double or long variable are encoded into two 32-bit quantities is implementation-dependent. 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文能力需加强下,看的速度比较慢。对不起了老师,大学后基本没好好学英文
评论
修改引用是一个原子操作,但是就像你后面提到的,没有happen-before的关系,所以仍然会存在多线程问题
如果改成volatile就OK了
并不能保证线程B看到这个修改
既然赋值是一个原子操作,那么看到没有看到又有什么关系呢?
或者说这个例子没有让我看到需要volatile的必要
恩,在这个场景下,不加volatile就是一个最终状态一致性的结果。可能两个线程会在两个不同的map中进行一个查找数据的过程,最终会达成一致状态。
如果你可以接受最终一致性,的确可以不需要volatile。 但比如使用一些变量做为标志位时,比如是否已经启动或者已经关闭等,对数据一致性有着比较高的要求,volatile就有其存在的必要性
给引用赋值是一个原子操作的含义只能保证引用地址的正确性,也就是说不会出现并发给long或double赋值时导致值的最终状态不一致。
但是,我想说的重点在下面
如果线程A给自己工作内存中的引用重新赋值,但是这个新的引用地址没有同步到共享内存中,那么线程B使用的引用就仍然是修改之前的,这样就导致两个线程访问的cache在某个时间段是不同的。
而使用volatile就会直接修改共享内存中的引用,所以可以解决这个问题
修改引用是一个原子操作,但是就像你后面提到的,没有happen-before的关系,所以仍然会存在多线程问题
如果改成volatile就OK了
并不能保证线程B看到这个修改
既然赋值是一个原子操作,那么看到没有看到又有什么关系呢?
或者说这个例子没有让我看到需要volatile的必要
恩,在这个场景下,不加volatile就是一个最终状态一致性的结果。可能两个线程会在两个不同的map中进行一个查找数据的过程,最终会达成一致状态。
如果你可以接受最终一致性,的确可以不需要volatile。 但比如使用一些变量做为标志位时,比如是否已经启动或者已经关闭等,对数据一致性有着比较高的要求,volatile就有其存在的必要性
修改引用是一个原子操作,但是就像你后面提到的,没有happen-before的关系,所以仍然会存在多线程问题
如果改成volatile就OK了
并不能保证线程B看到这个修改
既然赋值是一个原子操作,那么看到没有看到又有什么关系呢?
或者说这个例子没有让我看到需要volatile的必要
修改引用是一个原子操作,但是就像你后面提到的,没有happen-before的关系,所以仍然会存在多线程问题
如果改成volatile就OK了
Class SimpleCache { private Map cache = new HashMap() ; public Object get(String key) { return cache.get(key); } public void reload(){ Map tempCache = loadFromDB(); cache = tempCache; // 位置1,引用切换 } }
java里面的assgin操作都是原子的,除了long,double,这个你也提到了,那么
Map tempCache = loadFromDB(); 肯定是原子的;cache = tempCache也肯定是原子的。既然都是原子的,那一致性指的是什么呢? reload多线程问题,也就只是不知道最后cache是哪个线程创建而已吧。。。。但是返回的结果还是正确的。
对比我想起了DCL,加volatile
我很疑惑
发表评论
-
yugong QuickStart
2016-03-05 01:52 0几点说明 a. 数据迁移的方案可参见设计文档,oracl ... -
阿里巴巴开源项目: 阿里巴巴去Oracle数据迁移同步工具
2016-03-05 18:29 6509背景 08年左右,阿里巴巴开始尝试MySQL的相关 ... -
愚公performance
2016-03-02 17:29 0性能测试 全量测试 场景1 (单主键, ... -
yugong AdminGuide
2016-03-02 16:40 0环境要求 操作系统 数据库 迁移方案 部署 ... -
Tddl_hint
2014-01-27 13:52 0背景 工作原理 Hint格式 direct模 ... -
tddl5分库规则
2014-01-26 14:41 0背景 工作原理 构建语法树 元数据 基于 ... -
tddl5优化器
2014-01-22 15:12 0背景 工作原理 构建语法树 元数据 抽象语 ... -
Canal BinlogChange(mariadb5/10)
2014-01-20 17:25 4587背景 先前开源了一个 ... -
asynload quickstart
2013-10-08 22:49 0几点说明: 1. asyncload是做为一个j ... -
网友文档贡献
2013-09-18 15:50 01. Otter源代码解析系列 链接:http://e ... -
Manager配置介绍
2013-09-16 13:00 0通道配置说明 多种同步方式配置 a. 单向同步 ... -
canal&otter FAQ
2013-09-05 17:30 0常见问题 1. canal和 ... -
阿里巴巴开源项目:分布式数据库同步系统otter(解决中美异地机房)
2013-08-22 16:48 40433项目背景 阿里巴巴B2B公司,因为业务的特性 ... -
Otter AdminGuide
2013-08-19 11:06 0几点说明 otter系统自带了manager,所以简化了一 ... -
Otter高可用性
2013-08-17 23:41 0基本需求 网络不可靠,异地机房尤为明显. man ... -
Otter数据一致性
2013-08-17 23:39 0技术选型分析 需要处理一致性的业务场景: 多地修改 ( ... -
Otter扩展性
2013-08-17 22:20 0扩展性定义 按照实现不同,可分为两类: 数据处理自定 ... -
Otter双向回环控制
2013-08-17 21:37 0基本需求 支持mysql/oracle的异构数据库的双 ... -
Otter调度模型
2013-08-17 20:13 0背景 在介绍调度模型之前,首先了解一下otter系统要解 ... -
Otter Manager介绍
2013-08-16 11:16 0背景 otter4.0发布至 ...
相关推荐
MyObject obj = new MyObject(); String json = gson.toJson(obj); MyObject newObj = gson.fromJson(json, MyObject.class); ``` 2. Jackson:由 FasterXML 维护的一个高性能的库,支持JSON处理的多种模式,包括...
obj = new Program(); dynamic dyn; dyn = new Program(); //var v; v = new Program(); // 错误写法。正确写法如下: var v = new Program(); // 定义的同时必须指明初始化类型。 // 等价于:Program v = new ...
MyObject obj = new MyObject(); Intent intent = new Intent(currentActivity, NextActivity.class); intent.putExtra("my_object", obj); startActivity(intent); // 在NextActivity中接收 MyObject receivedObj ...
MyStruct obj = new MyStruct { Id = 1, Name = "test" }; // 序列化对象 using (MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream...
MyObject obj = new MyObject(); String jsonString = mapper.writeValueAsString(obj); MyObject newObj = mapper.readValue(jsonString, MyObject.class); ``` 2. Gson:Google开发的Gson库提供类似的转换功能。`...
ComplexObject obj = new ComplexObject(); obj.name = name; return obj; } public static ComplexObject createWithNameAndAge(String name, int age) { ComplexObject obj = new ComplexObject(); obj....
object obj = new object(); void SafeMethod() { lock (obj) { // 临界区代码 Console.WriteLine("Critical Section"); } } ``` 在这个例子中,通过 `lock` 语句自动实现了 `System.Monitor.Enter` 和 `...
MyObject obj = new MyObject(); File sdCard = Environment.getExternalStorageDirectory(); File dir = new File(sdCard.getAbsolutePath() + "/MyAppFolder"); dir.mkdirs(); File file = new File(dir, "my...
然而,ArrayList本身并不是线程安全的,这意味着在多线程环境中,多个线程同时访问和修改ArrayList时,可能会出现数据不一致或者竞态条件等问题。本测试着重于分析ArrayList在并发环境下的行为,并探讨如何确保其...
object obj = new object(); lock (obj) { // 共享资源的代码 } ``` 这里,`obj`作为锁对象,确保同一时间只有一个线程能执行该代码块。 三、`Monitor`类 `Monitor`提供了更底层的线程同步功能,可以用于获取和...
equals(Object) 方法的标准形式是 public boolean equals(Object obj),它返回一个布尔值,表示两个对象是否相等。在 Object 类中定义的 equals(Object) 方法只是简单地使用 == 运算符来比较两个对象的引用。因此,...
object obj = new object(); lock (obj) { // 临界区内代码 } ``` - **Monitor类**:该类提供了更高级别的同步功能,包括`Enter()`和`Exit()`方法,用于手动管理锁定过程。 示例代码: ```csharp object ...
高效的JSON与Object互转的工具源代码,如果JSON数据未按...2. Object obj = JSONTool.convertJsonToObject(jsonStr); 缺点:对于集合(Collection,非Map)类数据结构,不能在集合中包含其他集合对象,但是可以包含数组。
存储过程的应用+数据库 SqlParameter[] pr ={new SqlParameter("@name",SqlDbType.... object obj = com.ExecuteScalar(); if (obj != null) return Convert.ToInt32(obj.ToString()); else return 0; } }
myObj obj = new myObj("单位 1", "danwei1"); vector.add(obj); obj = new myObj("单位 2", "danwei2"); vector.add(obj); obj = new myObj("单位 3", "danwei2"); vector.add(obj); obj = new myObj("单位 ...
MyObject obj = new MyObject() { Property1 = "Value1", Property2 = 2 }; using (Stream stream = File.Create("data.bin")) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream...
MyObject obj = new MyObject(); Intent intent = new Intent(this, TargetActivity.class); intent.putExtra("key", obj); startActivity(intent); ``` 接收端: ```java Intent intent = getIntent(); MyObject ...
Object obj = new NewType(); NewType newValue = obj as NewType; ``` 在这个例子中,如果 `obj` 可以被转换为 `NewType` 类型,则 `newValue` 将被赋值为 `obj` 的转换结果;否则,`newValue` 将被设置为 `null`...
Object obj = beanClass.newInstance(); org.apache.commons.beanutils.BeanUtils.populate(obj, map); return obj; } public static Map, ?> objectToMap(Object obj) { if (obj == null) { return null; } ...
或者 var obj = new Object(); 为对象加入属性,方法: //=====第一种写法==================================== obj.name = ‘小明’; //为对象加属性 obj.updateName = function(name){//为对象定义updateName...