Java 多线程 原子性 Java编程思想读书笔记
来源:互联网 发布:python twisted下载 编辑:程序博客网 时间:2024/06/16 01:44
在有关Java线程的讨论中,常有一个不正确的知识是“原子操作不需要进行同步控制”。原子操作是不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前执行完成。但是注意:依赖于原子性是很棘手且很危险的。如果 你是一个并发专家,或者你得到来自这样的专家的帮助,你才应该使用原子性来代替同步。
Goetz测试:如果你可以编写用于现代微处理器的高性能JVM,那么就有资格去考虑是否可以避免同步。
原子性可以应用于除long和double之外的所有基本类型之上的“简单操作”。对于读取和写入除long和double之外的基本类型变量这样的操作,可以保证它们会被当作不可分(原子)的操作来操作内存。但JVM可以将64位(long和double变量)的读取和写入当作两个分享的32位操作来执行,这就产生了在一个读取和写入操作中间发生上下文切换,从而导致不同的任务可以看到不正确结果的可能性(有时被称为字撕裂)。但你定义long或double变量时,如果使用volatile关键字,就会获得(简单的赋值与返回操作的)原子性(注意:在Java SE5之前,volatile一直未能正确地工作)。不同的JVM可以任意地提供更强的保证,但你不应该依赖于平台相关的特性。
原子操作可由线程机制来保证其不可中断,专家级的程序员可以利用这一点来编写无锁的代码,这些代码不需要被同步。但是即使是这样,它也是一种过于简化的机制。有时,甚至起来应该是安全的原子操作,实际上也可能不安全。尝试移除同步通常是一种表示不成熟优化的信号,并且将会给你招致大量的麻烦,而你却可能没有收获多少好处,甚至完成没有。
一个任务做出的修改,即使在不中断的意义上讲是原子性的,对其他任务也可能是不可视的(例如,修改只是暂时性存储在本地处理器的缓存中,而其他任务看不到这修改的结果),导致不同的任务对应用的状态有不同的视图。另一方面,同步机制应强制在处理器系统中,一个任务做出的修改必须在应用中是可视的。如果没有同步机制,那么修改时可视将无法确定。
volatile关键字还确保了应用中的可视性。如果将一个域声明为volatile的,那么只要对这个域产生了写操作,那么所有的读操作都可以看到这个修改,即使使用了本地缓存,情况也如此,volatile域会立即被写入主存中,而读取操作就发生在主存中。
一个任务所作的任何写入操作对这个任务来说都是可视的,如果它只需要在这个任务内部可视,那么你就不需要将其设置为volatile了。
当一个域的值依赖于它之前的值时(例如递增一个计数器),volatile就无法工作了。如果某个域的值受到其他域的限制,那么volatile就无法工作了,例如Range类的lower和upper边界就必须遵循lower<=upper的限制。
使用volatile而不是synchronized的唯一安全情况是类中只有一个可变的域。再次提醒,你的第一选择应该是使用synchronized关键字,这是最安全的方式,而尝试其他任何方式都是有风险的。
什么才属于原子操作呢?对域中的值做赋值和返回操作通常都是原子性。
注意:如i++; i+=2; 等不是原子性的。
请看下面例子:
import java.util.concurrent.*; public class AtomicityTest implements Runnable{ private int i = 0; public int getValue(){ return i; } private synchronized void evenIncrement(){ i++; i++; } public void run(){ while(true) evenIncrement(); } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); AtomicityTest at = new AtomicityTest(); exec.execute(at); while(true){ int val = at.getValue(); if(val % 2 != 0){ System.out.println(val); System.exit(0); } } } }
该程序会找到奇数并终止。尽管return i确实是原子性操作,但是缺少同步使得其数值可以在处于不稳定的中间状态时被读取,除此之外,由于i也不是volatile的,因此还存在可视性问题,getValue()和evenIncrement()必须是synchronized的。
原子类
Java SE5 引入了诸如AtomicInteger,AtomicLong,AtomicReference等特殊的原子性变量类,它们提供了下面形式的原子性条件更新操作:
boolean compareAndSet(int expect, int update);
这些类被调整为可以使用在某些现代处理器上的获得的,并且是在机器级别上的原子性,因此在使用它们时,通常不需要担心。对于常规编程来说,它们很少会派上用场,但是在涉及性能调优时,它们就大有用武之地了。例如,可以使用AtomicIntegerTest重写AtomicityTest:
import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerTest implements Runnable{ private AtomicInteger i = new AtomicInteger(0); public int getValue(){ return i.get(); } private void evenIncrement(){ i.addAndGet(2); } public void run(){ while(true){ evenIncrement(); } } public static void main(String[] args) { new Timer().schedule(new TimerTask() { public void run() { System.err.println("Aborting"); System.exit(0); } }, 5000); ExecutorService exec = Executors.newCachedThreadPool(); AtomicIntegerTest ait = new AtomicIntegerTest(); exec.execute(ait); while(true){ int val = ait.getValue(); if(val % 2 != 0){ System.out.println(val); System.exit(0); } } } }通过AtomicInteger消除了synchronized关键字。因为这个程序不会失败,所以添加了一个Timer,以便在5秒钟之后自动终止。
- Java 多线程 原子性 Java编程思想读书笔记
- Java 多线程 同步 Java编程思想读书笔记
- Java 多线程 死锁 Java编程思想读书笔记
- java编程思想读书笔记-第十四章 多线程
- Java编程思想读书笔记
- java编程思想读书笔记
- java 编程思想 读书笔记
- <<java编程思想>>读书笔记
- java编程思想读书笔记
- java编程思想读书笔记
- 《Java编程思想》读书笔记
- Java 编程思想 - 读书笔记
- JAVA编程思想读书笔记
- 《java编程思想》读书笔记
- java编程思想读书笔记
- java 编程思想 读书笔记
- 《Java编程思想》读书笔记
- Java 多线程 基础入门 Java编程思想读书笔记
- Mali & UMP memory allocation
- [FOJ 2105][Vjudge 35179] Digits Count [线段树]
- ajax基础
- JavaScript学习笔记 第五记
- Android开发全程记录(十二)——ImageView 加边框的方法
- Java 多线程 原子性 Java编程思想读书笔记
- crawler4j源码分析(四)Parser
- python登陆Discuz!论坛通用代码
- php 解决MySQL插入数据出现 Incorrect string value: '\xF0\x9F\x92\x8BTi...'错误
- union关键字
- python try except else finally 执行顺序详细分析
- 数据结构学习12——快速排序
- [golang]自己动手实现ini文件读取
- JFreeChart的使用