Lou*_*man 129
volatile关键字的作用大致是对该变量的每个单独的读或写操作都是原子的.
然而,值得注意的是,需要多次读/写的操作(例如i++,相当于i = i + 1一次读取和一次写入)不是原子操作,因为另一个线程可能i在读取和写入之间写入.
Atomic类AtomicInteger和的类AtomicReference以原子方式提供更多种类的操作,具体包括增量AtomicInteger.
Two*_*The 74
挥发性和原子性是两个不同的概念.Volatile确保在不同的线程中某个预期(内存)状态为真,而Atomics确保对变量的操作以原子方式执行.
以Java中的两个线程为例:
线程A:
value = 1;
done = true;
Run Code Online (Sandbox Code Playgroud)
线程B:
if (done)
System.out.println(value);
Run Code Online (Sandbox Code Playgroud)
与启动value = 0和done = false线程的规律告诉我们,它是未定义与否线程B将打印值.此外,价值在这一点上也是不确定的!为了解释这一点,您需要了解一下Java内存管理(可能很复杂),简而言之:线程可能会创建变量的本地副本,而JVM可以重新排序代码以对其进行优化,因此无法保证上述代码按顺序运行.将done设置为true 然后将值设置为1可能是JIT优化的可能结果.
volatile只确保在访问此类变量时,新值将立即对所有其他线程可见,并且执行顺序确保代码处于您期望的状态.因此,在上面的代码中,定义done为volatile将确保每当线程B检查变量时,它都是false或true,如果为true,则value也设置为1.
作为volatile的副作用,这种变量的值在线程范围内以原子方式设置(执行速度非常小).然而,这对于使用长(64位)变量(或类似)的32位系统来说非常重要,在大多数情况下,设置/读取变量无论如何都是原子的.但原子访问和原子操作之间存在重要区别.Volatile仅确保访问是原子的,而Atomics确保操作是原子的.
请看以下示例:
i = i + 1;
Run Code Online (Sandbox Code Playgroud)
无论你如何定义i,在执行上述行时读取值的另一个Thread可能得到i或i + 1,因为该操作不是原子的.如果另一个线程将i设置为不同的值,在最坏的情况下,我可以将其设置回线程A之前的任何值,因为它只是在基于旧值计算i + 1的中间,然后设置i再次到那个旧值+ 1.说明:
Assume i = 0
Thread A reads i, calculates i+1, which is 1
Thread B sets i to 1000 and returns
Thread A now sets i to the result of the operation, which is i = 1
Run Code Online (Sandbox Code Playgroud)
像AtomicInteger这样的原子学确保这种操作以原子方式发生.所以上面的问题不可能发生,一旦两个线程完成,我要么是1000或1001.
Try*_*ing 50
在多线程环境中有两个重要的概念.
Volatile消除可见性问题,但它不涉及原子性.Volatile将阻止编译器重新排序涉及写入和随后读取volatile变量的指令.例如,k++
这k++不是一个机器指令,而是三个机器指令.
所以,即使你声明变量,volatile它也不会使这个操作成为原子,这意味着另一个线程可以看到一个中间结果,这是另一个线程的陈旧或不需要的值.
但是AtomicInteger,AtomicReference它们基于Compare和swap指令.CAS有三个操作数:要操作的内存位置V,预期的旧值A和新值B.CAS原子地更新V为新值B,但仅当值V与预期的旧值匹配时才会更新A; 否则它什么都不做.在任何一种情况下,它都会返回当前的值V.这由JVM使用AtomicInteger,AtomicReference它们将函数称为compareAndSet().如果底层处理器不支持此功能,则JVM通过自旋锁实现它.
sco*_*ttb 23
正如所示尝试,volatile仅处理可见性.
在并发环境中考虑此代码段:
boolean isStopped = false;
:
:
while (!isStopped) {
// do some kind of work
}
Run Code Online (Sandbox Code Playgroud)
这里的想法是,某些线程可以将isStoppedfalse 的值更改为true,以便向后续循环指示是时候停止循环.
直观地说,没有问题.逻辑上,如果另一个线程isStopped等于true,则循环必须终止.实际情况是,即使另一个线程isStopped等于true ,循环也可能永远不会终止.
其原因并不直观,但考虑到现代处理器具有多个内核,并且每个内核具有多个寄存器和多级别的高速缓存,其他处理器无法访问.换句话说,在一个处理器的本地内存中缓存的值对于在不同处理器上执行的线程是不可见的.这是并发的核心问题之一:可见性.
Java内存模型无法保证何时对线程中的变量所做的更改可能对其他线程可见.为了保证更新在发布后立即可见,您必须进行同步.
该volatile关键字是同步的弱形式.虽然它对互斥或原子性没有任何作用,但它确实提供了保证,一个线程中对变量的更改一旦生成就会对其他线程可见.因为对Java中非8字节变量的单独读取和写入是原子的,所以声明变量volatile提供了一种简单的机制,可以在没有其他原子性或互斥要求的情况下提供可见性.
Ort*_*ier 13
使用volatile关键字:
long和double.(所有其他的,原始访问已经保证是原子的!)java.util.concurrent.atomic.*根据java文档,这些类是:
一个小型工具包,支持对单个变量进行无锁线程安全编程.本质上,此包中的类将volatile值,字段和数组元素的概念扩展为也提供表单的原子条件更新操作的概念:
boolean compareAndSet(expectedValue, updateValue);
原子类是围绕原子compareAndSet(...)函数构建的,该函数映射到原子CPU指令.原子类引入了变量之前发生的事件volatile.(有一个例外:) weakCompareAndSet(...).
来自java文档:
当线程看到由weakCompareAndSet引起的原子变量更新时,它不一定会看到对weakCompareAndSet之前发生的任何其他变量的更新.
对于你的问题:
这是否意味着,凡发生在它的锁,将要设置其值.如果同时,当第一个线程更改其值时,其他一些线程会出现并读取旧值,那么新线程是否会读取其旧值?
你没有锁定任何东西,你所描述的是一种典型的竞争条件,如果线程在没有正确同步的情况下访问共享数据,它最终会发生.如前所述,volatile在这种情况下声明变量只会确保其他线程会看到变量的变化(该值不会缓存在某个缓存的寄存器中,只能被一个线程看到).
AtomicInteger和之间有什么区别volatile int?
AtomicInteger提供了一个原子操作int以适当的同步(例如incrementAndGet(),getAndAdd(...)...),volatile int将只保证的知名度int给其他线程.
Pet*_*rey 12
那么如果两个线程同时攻击一个易失性原始变量会发生什么呢?
通常每个人都可以增加值.但是有时候,两者都会同时更新值而不是递增2,而是增加1和1的线程增量.
这是否意味着,凡发生在它的锁,将要设置其值.
没有锁.这synchronized是为了什么.
如果同时,当第一个线程更改其值时,其他一些线程会出现并读取旧值,那么新线程是否会读取其旧值?
是,
Atomic和volatile关键字有什么区别?
AtomicXxxx包含一个volatile,因此它们基本相同,不同之处在于它提供了更高级别的操作,例如用于实现增量的CompareAndSwap.
AtomicXxxx还支持lazySet.这就像一个易失性集,但不会使管道停止等待写完成.这可能意味着如果你读到一个你刚写的值,你可能会看到旧值,但你不应该这样做.不同之处在于设置volatile需要大约5 ns,bit lazySet大约需要0.5 ns.
| 归档时间: |
|
| 查看次数: |
88866 次 |
| 最近记录: |