线程安全学习笔记(二)
两个常来说明volatile的例子:
例子1:
测试类:
输出结果:
All threads count 1698 times!!
Thread 0 count 1000 times!
Thread 1 count 1000 times!
例子2:package test;import java.util.Vector;import com.maximilian.www.MyTestThread2;public class MyTest2{private static int threadNum = 2;private static int threadId;private static Vector<MyTestThread2> threads = new Vector<MyTestThread2>();public static void main (String [] args){ for(threadId=0;threadId < threadNum ;threadId++) { MyTestThread2 t = new MyTestThread2(threadId); threads.add(t); } for(Thread t:threads) { t.start(); } for(Thread t:threads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("lowerLimit:"+MyTestThread2.getLowerLimit()+"\n upperLimit:"+MyTestThread2.getUpperLimit());}}
测试结果:
lowerLimit:4
upperLimit:3
从上面的两个例子可以看出, volatile修饰的变量至少从操作的角度来看不是线性进行的。
一般的普通的对变量的操作过程:
多线程的情况下就会发生:
通过上面例子,我们可以知道,即使对变量进行了volatile申明还是有上面图中的现象寻在,例子1中计数操作有线程的操作直接被覆盖,对于例子2两个有内在联系的变量的操作也是因为这种读取-修改-更新的非原子性导致的,有点写逻辑的时候因为延时出现的竞争冒险现象(写逻辑出身伤不起)。
那么volatile加了有什么作用的?对volatile变量在线程本地工作区中不做缓存,对volatile的读写总是指向堆中的引用。这个类似于告诉线程这个变量在本身的存储空间上的值是不可信的,每次要用到就要去公共内存上读取,并且会要求线程在修改完成后及时更新住内存(有些操作对对象连续操作可能不会及时每变一次更新),这样就保证了线程的操作只要完成就可以被其他线程看到。
Volatile对于新手来说比较难用,但是因为他的性能好于syncronized以及没有锁的同步,在高手手里还是个宝。
应用场景:
1.状态标志指示发生了一个重要的一次性事件,例如完成初始化或请求停机;
2.一次性安全发布。在1.5以后貌似是可以解决双重检查锁定单例中的问题。
http://www.cnblogs.com/melode11/archive/2008/09/28/1301114.html
http://www.ibm.com/developerworks/cn/java/j-dcl.html
3.独立观察,定期 “发布” 观察结果供程序内部使用。
4.volatile bean 模式。
5.结合volatile和syncronized的开销较低的读-写锁策略。