JAVA 原子性和波动性总结 Atomicity and Volality

JAVA 原子性和波动性总结 Atomicity and Volality原子性 Atomicity nbsp 简单来说就是指不能被中断的一次性操作 在 JAVA 中 除了 long 和 double 的基本类型的读和写都是原子操作 这里要注意 long 和 double 是 64 位的 他们的操作被分割为两个 32 位操作 因此对 long 和 double 的读和写不是原子操作 非原子操作在多线程环境中是会出问题的 不能达到预期 例如写一个函数 每次都返回某个变量 1 想要实现不同的线程

原子性Atomicity  

简单来说就是指不能被中断的一次性操作。

在JAVA中,除了long 和double的基本类型的读和写都是原子操作。这里要注意,long 和double是64位的,他们的操作被分割为两个32位操作,因此对long 和double的读和写不是原子操作。

非原子操作在多线程环境中是会出问题的,不能达到预期。

例如写一个函数,每次都返回某个变量+1,想要实现不同的线程调用它的时候都能得到不同的值,这个是做不到的。

因此在多线程中,要使用同步机制,来确保执行某个非原子操作时候对资源进行锁定,从而实现预期的效果。

Volality

原子操作在多线程中是否能达到预期?

答案是否定的,因为现在的CPU都是多核的,每个核心都会有寄存器或者缓存,某个task对某个值进行原子性操作,但此时可能另一个task运行在另一个核中,并不能识别到前面task的操作,因此尽管操作是原子性的,但不同task仍可能视角不一致。这就是Volality。翻译成 挥发性 或者 波动性

如果使用同步机制,是可以确保不发生这种情况的。

如果不用同步机制,JAVA提供了volatile修饰符,它可以保证某个field写入时候,所有的读操作都能识别到。也就是说写入时候会强制写入主内存,并迫使所有的读都从主内存中读取。

因此这里可以看出 原子性和波动性是两个不同的概念。在单线程中是不需要用volatile的。

如果不用同步机制,那么当某field依赖它之前的值时候,volatile是不生效的。(因为这个操作是非原子性操作,不能达到预期)

看以下例子:

public class AtomicityTest implements Runnable{ private int i = 0;//加 volatile 后程序无影响 public int getValue(){ return i; } private synchronized void evenIncrement(){ i++; i++; } @Override 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.print(val); System.exit(0); } } } }

i 每次都加2,希望实现i都是偶数,运行后会发现可以读到i是奇数的情况。这里即便用了volatile来修饰,而且对写的方法也加了同步机制,但仍不能达到预期。

解决办法是,对get 读的方法也使用同步机制。

原子类 

JAVA SE5 提供了原子类,用于提供原子操作,有AtomicInteger AtomicLong AtomicReference等

提供一些原子方法,我们用AtomicInteger 改造上面代码

public class AtomicIntTest implements Runnable{ private volatile AtomicInteger i = new AtomicInteger(0); public int getValue(){ return i.get(); } private void evenIncrement(){ i.addAndGet(2); } @Override public void run() { while(true){ evenIncrement(); } } public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); AtomicIntTest at = new AtomicIntTest(); exec.execute(at); while(true){ int val = at.getValue(); if(val % 2 !=0){ System.out.print(val); System.exit(0); } } } }

可见,通过使用原子类,不使用同步机制也可以实现预期功能。

总之,可以尽量使用原子类,在不能用原子类的时候使用同步机制是可以实现预期功能的,如果你的知识足够丰富,可以使用volatile尝试去进行优化,但它只适用于少量特殊场景。



版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/227730.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午8:40
下一篇 2026年3月16日 下午8:40


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号