Java多线程 之 原子性与可见性(八)
2016-06-19 19:00
274 查看
1.原子性
java中的原子性,是指:原子操作是不能被线程调度机制中断的;操作一旦开始,它一定会在可能发生的“上下文切换”(即切换到其他线程执行)之前执行完毕。但是千万不要认为“原子操作不需要同步控制(这是错误的)”!
原子性可以应用于除long和double之外的基本类型的简单操作(赋值和返回值)。long和double这种64位的数据类型,在JVM内部是通过两条32位的操作完成的,因此有可能发生上下文切换。如果给long和double变量加上volatile关键字也可以获得原子性。
但是原子性并不能保证并发的代码的正确性。比如,在多处理器(可能是单处理器但是多核)多线程环境下,一个线程对某一变量的写入操作有可能只是将这种变化存在了CPU缓存中,而其他线程对该变量的访问只是局限在各自的CPU缓存,这样导致了不一致性。因此,还需要使用volatile关键字来保证可见性,将变量的修改直接写入到内存中。
当然同步(加锁)机制也可以保证这种可见性,将变量的修改直接写到内存中。
在同一个任务中,可见性问题是不存在的。即该任务对变量的修改,该任务肯定知道。
但是,当一个属性的值依赖于它之前的值时(如递增操作),一个属性的值依赖于其他域的值的限制,volatile就无法工作了。这里说的不能工作了,应该是指在并发环境下无法保证代码的正确性吧。
我的理解:原子性+volatitle可以保证并发的正确性。但是,最好编码时还是尽量用 同步加锁 来保证并发的正确性。第一选择应该是synchronized关键字。
再次声明:在Java中,自增操作不是原子的。
在《Thinking in java》第四版中文版的682-684页举例详细说明下面两点:
(1)在java中,对除long和double之外的基本类型的简单操作(赋值、返回值)是原子性的,但是无法保证并发的正确性。
(2)在java中,递增操作不是原子性的,会引发并发问题。
2.原子类Atomic
在JDK5之后,引入了AtomicInteger、AtomicLong、AtomicReference等原子类。这些类其实是用来构建java.util.concurrent中的类的。也就是说,这些类是JUC实现的基础类,JUC构建在Atomic之上,JUC的并发是用Atomic来实现的。我们在写代码时,要首先考虑使用synchronized关键字和Lock对象,而避免使用Atomic类。当涉及到调优时,可以考虑使用Atomic类。Atomic类要比synchronized、Lock更高效。
下面使用Atomic类来重写EvenGenerator:
package org.fan.learn.thread.share; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicInteger; /** * Created by thinkpad on 2016/6/19. */ public class AtomicEvenGenerator extends IntGenerator { private AtomicInteger atomicInteger = new AtomicInteger(0); @Override public int next() { return atomicInteger.addAndGet(2); } public static void main(String[] args) { new Timer().schedule(new TimerTask() { @Override public void run() { System.err.println("Aborting"); System.exit(0); } }, 5000); //执行5s后自动终止 EvenChecker.test(new AtomicEvenGenerator()); } }
这里使用Timer,使程序运行5s之后自动终止。
运行结果如下:
Press control-c to exit
Aborting
关于EvenGenerator的其他部分可参考下面两篇博文:
Java多线程 之 访问共享资源(六)
Java多线程 之 访问共享资源synchronized、lock(七)
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Python3写爬虫(四)多线程实现数据爬取
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序