并发(二):理解volatile
来源:互联网 发布:2016超级女声网络投票 编辑:程序博客网 时间:2024/06/16 09:30
在JAVA中,当一个对象被生命为volatile时,我们说这个对象对所有的线程是可见的,那么这个对象具体指什么?是指这个对象引用的地址,还是指这个对象的内容,或者两者都包含?
1. 对象的地址是始终可见的
如果将一个对象声明为volatile,那么这个对象的引用无论发生任何变化,其他线程都可以立即感知它的变化,那么依照此推论,volatile与final修饰词必定是互斥的,二者只能任选其一,如下必定就是错误的声明:
// 编译器提示The field yiifaa can be either final or volatile, not bothprivate static volatile final Person yiifaa = new Person();
对于集合而言,如果声明为volatile,如果要感知到里面元素的变化,最可靠的方式是,每次都要重新对集合重新赋值,如下:
private static volatile List<String> names = Lists.newArrayList();// 在调用时必须重新赋值,才能发挥volatile的作用// 以下代码在方法中调用names = Lists.newArrayList("yiifaa");
在上述的例子中,如果调用List的add、remove方法,对其他线程而言,其内容并不一定是可见的,所以常常出现难以预料的效果:
// 错误的写法names.add("yiifaa");
2. 对象的内容(属性)并不是可见的
在并发编程的实践里,证明并发程序执行正常是非常难的(即使出现了正确的结果,也不一定能够证明在所有的实践中都是正常的),所以通常证明这么做可能会导致错误。
拟定下面的测试类:
static class Person { private Long age = 0L; // 总是递增 public void increment() { // 如果是可见的,age的读取与写入应该可以保持一致 age ++; } public long getAge() { return age; }}
测试代码:
private static volatile Person yiifaa = new Person();public static void main(String[] args) throws InterruptedException { // 创建四个线程计数,每个线程递增25次,攻击4 * 25=100 for(int i = 0; i < 4; i ++) { new Thread() { public void run() { int count = 25; // 计数25次 while(count > 0) { // 递增数字 yiifaa.increment(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } count --; } } }.start(); } Thread.sleep(3000); // 应该输出100 System.out.println(yiifaa.getAge());}
如果volatile的内容也是可见的,那么yiifaa.getAge()输出结果应该是100,但是实践中,数字远远达不到100,只有80左右(识机器而定)。
因此,volatile修饰对象的内容不是可见的,修正的方法很简单,对increment与getAge进行同步即可,如下:
static class Person { private Long age = 0L; // 对读写同步 public synchronized void increment() { age ++; } // 对读写同步 public synchronized long getAge() { return age; }}
结论
千万不要以为volatile修饰的对象就是线程安全的,尤其是修饰的对象还是包含内蕴状态的情况,正确的做法是要么对修饰对象直接赋值,要么将修饰对象的功能限制为某个操作完成、发生中断或状态的标志。
最后值得一提的还有AtomicReference,作用与volatile类似,只能保证对象引用的可见性,不能保证对象内容的可见性。
- 并发(二):理解volatile
- Java并发volatile理解
- Volatile并发理解
- Java并发之volatile二
- Java并发编程之volatile的理解
- Java并发编程--深入理解volatile关键字
- java并发(二) volatile关键字解析
- 并发编程(二) volatile的应用
- Java多线程并发编程之二volatile
- Java并发编程(二)——volatile
- 高并发基础之volatile(二)
- java并发编程学习(二) volatile
- Java并发编程之volatile关键字的理解
- Java并发编程之volatile关键字的理解与使用
- 理解高并发(19).volatile原理及用法
- 深入理解Java并发机制(2)--volatile关键字
- java并发(二十六)正确使用Volatile变量
- 【Java并发编程】(二)——volatile
- 典型递归问题整理
- lucene 3(相关度排序)
- Android禁止ViewPager的左右滑动
- Java实现-搜索二维矩阵
- java 泛型
- 并发(二):理解volatile
- Android 用MediaCodec实现视频硬解码
- QAbstractSocket::waitForDisconnected() is not allowed in UnconnectedState
- 对于职业、软件学习的感想
- Hdu 2089 不要62
- 大型web系统数据缓存设计
- 设计模式(一)-策略模式
- servlet向客户端返回JSON数据
- 制作nginx+php的docker镜像方法