原子/挥发/同步内部是如何工作的?

下面的代码块有什么区别?

代码1

private int counter;

public int getNextUniqueIndex() {
    return counter++; 
}

代码2

private AtomicInteger counter;

public int getNextUniqueIndex() {
    return counter.getAndIncrement();
}

代码3

private volatile int counter;

public int getNextUniqueIndex() {
    return counter++; 
}

挥发物以以下方式工作吗?是

volatile int i = 0;
void incIBy5() {
    i += 5;
}

相当于

Integer i = 5;
void incIBy5() {
    int temp;
    synchronized(i) { temp = i }
    synchronized(i) { i = temp + 5 }
}

我认为两个线程不能同时进入同步块…我说的对吗?如果这是真的,那么如何atomic.incrementAndGet()工作没有同步?它是否线程安全?

内部读取和写入volatile变量/原子变量之间的区别是什么?我在一些文章中读到,线程有一个变量的本地副本-那是什么?


当前回答

Java volatile修饰符是一种特殊机制的例子,它可以保证线程之间进行通信。当一个线程写入一个易失性变量,而另一个线程看到这个写入,第一个线程就会告诉第二个线程关于内存中的所有内容,直到它执行对该易失性变量的写入。

原子操作在单个任务单元中执行,不受其他操作的干扰。在多线程环境中,原子操作是避免数据不一致的必要手段。

其他回答

同步Vs原子Vs Volatile:

Volatile and Atomic is apply only on variable , While Synchronized apply on method. Volatile ensure about visibility not atomicity/consistency of object , While other both ensure about visibility and atomicity. Volatile variable store in RAM and it’s faster in access but we can’t achive Thread safety or synchronization whitout synchronized keyword. Synchronized implemented as synchronized block or synchronized method while both not. We can thread safe multiple line of code with help of synchronized keyword while with both we can’t achieve the same. Synchronized can lock the same class object or different class object while both can’t.

如有遗漏,请指正。

Java volatile修饰符是一种特殊机制的例子,它可以保证线程之间进行通信。当一个线程写入一个易失性变量,而另一个线程看到这个写入,第一个线程就会告诉第二个线程关于内存中的所有内容,直到它执行对该易失性变量的写入。

原子操作在单个任务单元中执行,不受其他操作的干扰。在多线程环境中,原子操作是避免数据不一致的必要手段。

挥发性:

Volatile是一个关键字。Volatile强制所有线程从主存而不是缓存中获取变量的最新值。访问volatile变量不需要锁定。所有线程都可以同时访问volatile变量值。

使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会与该变量的后续读取建立happens-before关系。

这意味着对volatile变量的更改总是对其他线程可见。更重要的是,这也意味着当线程读取一个volatile变量时,它不仅看到了对volatile的最新更改,还看到了导致更改的代码的副作用。

何时使用:一个线程修改数据,其他线程必须读取数据的最新值。其他线程将采取一些操作,但它们不会更新数据。

原子XXX:

AtomicXXX类支持对单个变量进行无锁线程安全编程。这些AtomicXXX类(如AtomicInteger)解决了内存不一致错误/修改volatile变量的副作用,这些变量在多个线程中被访问。

何时使用:多个线程可以读取和修改数据。

同步:

Synchronized是用于保护方法或代码块的关键字。通过使方法同步有两个效果:

First, it is not possible for two invocations of synchronized methods on the same object to interleave. When one thread is executing a synchronized method for an object, all other threads that invoke synchronized methods for the same object block (suspend execution) until the first thread is done with the object. Second, when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads.

何时使用:多个线程可以读取和修改数据。您的业务逻辑不仅更新数据,还执行原子操作

AtomicXXX相当于volatile + synchronized,尽管实现不同。AmtomicXXX扩展了volatile变量+ compareAndSet方法,但不使用同步。

相关的SE问题:

Java中volatile和synchronized的区别

Volatile boolean vs AtomicBoolean

值得阅读的好文章:(以上内容摘自这些文档页面)

https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html

volatile +同步是一个完全原子化的操作(语句)的可靠解决方案,它包含了对CPU的多条指令。

Say for eg:volatile int i = 2; i++, which is nothing but i = i + 1; which makes i as the value 3 in the memory after the execution of this statement. This includes reading the existing value from memory for i(which is 2), load into the CPU accumulator register and do with the calculation by increment the existing value with one(2 + 1 = 3 in accumulator) and then write back that incremented value back to the memory. These operations are not atomic enough though the value is of i is volatile. i being volatile guarantees only that a SINGLE read/write from memory is atomic and not with MULTIPLE. Hence, we need to have synchronized also around i++ to keep it to be fool proof atomic statement. Remember the fact that a statement includes multiple statements.

希望这个解释足够清楚。

将变量声明为volatile意味着修改其值立即影响变量的实际内存存储。编译器不能优化掉对变量的任何引用。这保证当一个线程修改变量时,所有其他线程立即看到新值。(对于非易失性变量,不能保证这一点。)

Declaring an atomic variable guarantees that operations made on the variable occur in an atomic fashion, i.e., that all of the substeps of the operation are completed within the thread they are executed and are not interrupted by other threads. For example, an increment-and-test operation requires the variable to be incremented and then compared to another value; an atomic operation guarantees that both of these steps will be completed as if they were a single indivisible/uninterruptible operation.

对一个变量的所有访问同步一次只允许一个线程访问该变量,并强制所有其他线程等待访问该变量的线程释放其对该变量的访问。

同步访问类似于原子访问,但是原子操作通常在较低的编程级别上实现。此外,完全可以只同步对变量的某些访问,而允许其他访问不同步(例如,同步对变量的所有写操作,但不同步从变量中读取操作)。

原子性、同步性和波动性是独立的属性,但通常结合使用,以加强访问变量时的适当线程合作。

附录(2016年4月)

对变量的同步访问通常使用监视器或信号量实现。这些是低级互斥(互斥)机制,允许线程独占地获得对变量或代码块的控制,如果其他线程也试图获得相同的互斥,则强制所有其他线程等待。一旦拥有这个互斥量的线程释放了这个互斥量,另一个线程就可以依次获得这个互斥量。

附录(2016年7月)

对象发生同步。这意味着调用类的同步方法将锁定调用的This对象。静态同步方法将锁定Class对象本身。

同样,进入同步块需要锁定方法的this对象。

这意味着同步方法(或块)可以同时在多个线程中执行,如果它们锁定在不同的对象上,但是对于任何给定的单个对象,一次只能有一个线程执行同步方法(或块)。