原子/挥发/同步之间的区别是什么?

时间:2022-05-14 21:01:33

How do atomic / volatile / synchronized work internally?

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

What is the difference between the following code blocks?

以下代码块之间的区别是什么?

Code 1

代码1

private int counter;

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

Code 2

代码2

private AtomicInteger counter;

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

Code 3

代码3

private volatile int counter;

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

Does volatile work in the following way? Is

volatile挥发物是否以以下方式工作?是

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

equivalent to

相当于

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

I think that two threads cannot enter a synchronized block at the same time... am I right? If this is true then how does atomic.incrementAndGet() work without synchronized? And is it thread-safe?

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

And what is the difference between internal reading and writing to volatile variables / atomic variables? I read in some article that the thread has a local copy of the variables - what is that?

对挥发性变量/原子变量的内部读写有什么区别?我在某篇文章中读到线程有变量的本地拷贝——那是什么?

6 个解决方案

#1


307  

You are specifically asking about how they internally work, so here you are:

你问的是他们内部是如何运作的,所以你在这里:

No synchronization

private int counter;

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

It basically reads value from memory, increments it and puts back to memory. This works in single thread but nowadays, in the era of multi-core, multi-CPU, multi-level caches it won't work correctly. First of all it introduces race condition (several threads can read the value at the same time), but also visibility problems. The value might only be stored in "local" CPU memory (some cache) and not be visible for other CPUs/cores (and thus - threads). This is why many refer to local copy of a variable in a thread. It is very unsafe. Consider this popular but broken thread-stopping code:

它基本上是从内存中读取值,然后递增,然后返回到内存中。这可以在单线程中工作,但是现在,在多核、多cpu、多级别缓存的时代,它将无法正常工作。首先,它引入了竞争条件(多个线程可以同时读取值),同时还引入了可见性问题。该值可能只存储在“本地”CPU内存(某些缓存)中,而其他CPU /内核(以及线程)不可见。这就是为什么许多线程引用变量的本地拷贝。这是非常不安全的。考虑一下这种流行但破坏了的线程停止代码:

private boolean stopped;

public void run() {
    while(!stopped) {
        //do some work
    }
}

public void pleaseStop() {
    stopped = true;
}

Add volatile to stopped variable and it works fine - if any other thread modifies stopped variable via pleaseStop() method, you are guaranteed to see that change immediately in working thread's while(!stopped) loop. BTW this is not a good way to interrupt a thread either, see: How to stop a thread that is running forever without any use and Stopping a specific java thread.

将volatile添加到已停止的变量,它可以正常工作——如果任何其他线程通过pleaseStop()方法修改已停止的变量,您可以保证在工作线程的while(!stop)循环中立即看到该更改。顺便说一下,这并不是中断线程的好方法,请参见:如何停止不使用任何使用并停止特定java线程的线程。

AtomicInteger

private AtomicInteger counter = new AtomicInteger();

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

The AtomicInteger class uses CAS (compare-and-swap) low-level CPU operations (no synchronization needed!) They allow you to modify a particular variable only if the present value is equal to something else (and is returned successfully). So when you execute getAndIncrement() it actually runs in a loop (simplified real implementation):

AtomicInteger类使用CAS(比较和交换)低级CPU操作(不需要同步!)它们只允许在现值等于其他值(并成功返回)的情况下修改特定的变量。因此,当您执行getAndIncrement()时,它实际上在一个循环中运行(简化的实际实现):

int current;
do {
  current = get();
} while(!compareAndSet(current, current + 1));

So basically: read; try to store incremented value; if not successful (the value is no longer equal to current), read and try again. The compareAndSet() is implemented in native code (assembly).

所以基本上:阅读;尝试存储递增值;如果不成功(值不再等于当前值),请再次读取并重试。compareAndSet()是在本地代码(assembly)中实现的。

volatile without synchronization

private volatile int counter;

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

This code is not correct. It fixes the visibility issue (volatile makes sure other threads can see change made to counter) but still has a race condition. This has been explained multiple times: pre/post-incrementation is not atomic.

这段代码不正确。它修复了可见性问题(volatile确保其他线程可以看到为计数器而做的更改),但仍然存在竞争条件。这已经多次解释了:pre/后增量不是原子的。

The only side effect of volatile is "flushing" caches so that all other parties see the freshest version of the data. This is too strict in most situations; that is why volatile is not default.

volatile的唯一副作用是“刷新”缓存,这样所有其他方都可以看到数据的最新版本。这在大多数情况下都太严格了;这就是为什么volatile不会出现违约。

volatile without synchronization (2)

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

The same problem as above, but even worse because i is not private. The race condition is still present. Why is it a problem? If, say, two threads run this code simultaneously, the output might be + 5 or + 10. However, you are guaranteed to see the change.

和上面的问题一样,但更糟糕的是,因为我不是私人。种族状况仍然存在。为什么这是个问题?如果两个线程同时运行此代码,输出可能是+ 5或+ 10。但是,您肯定会看到更改。

Multiple independent synchronized

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

Surprise, this code is incorrect as well. In fact, it is completely wrong. First of all you are synchronizing on i, which is about to be changed (moreover, i is a primitive, so I guess you are synchronizing on a temporary Integer created via autoboxing...) Completely flawed. You could also write:

令人惊讶的是,这个代码也不正确。事实上,这是完全错误的。首先,您正在对i进行同步,它将被更改(而且,i是一个原语,所以我猜您正在对通过自动装箱创建的临时整数进行同步…)完全有缺陷的。你也可以写:

synchronized(new Object()) {
  //thread-safe, SRSLy?
}

No two threads can enter the same synchronized block with the same lock. In this case (and similarly in your code) the lock object changes upon every execution, so synchronized effectively has no effect.

没有两个线程可以使用相同的锁进入同一个同步块。在这种情况下(在您的代码中也是如此),锁对象在每次执行时都会发生变化,因此有效地同步没有效果。

Even if you have used a final variable (or this) for synchronization, the code is still incorrect. Two threads can first read i to temp synchronously (having the same value locally in temp), then the first assigns a new value to i (say, from 1 to 6) and the other one does the same thing (from 1 to 6).

即使您使用了一个最终变量(或这个)进行同步,代码仍然是不正确的。两个线程可以首先同步地将i读到temp(在temp中具有相同的本地值),然后第一个线程为i分配一个新值(例如,从1到6),另一个线程执行相同的操作(从1到6)。

The synchronization must span from reading to assigning a value. Your first synchronization has no effect (reading an int is atomic) and the second as well. In my opinion, these are the correct forms:

同步必须从读取扩展到分配值。第一个同步没有效果(读取int是原子的),第二个同步也一样。在我看来,这些是正确的形式:

void synchronized incIBy5() {
  i += 5 
}

void incIBy5() {
  synchronized(this) {
    i += 5 
  }
}

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

#2


38  

Declaring a variable as volatile means that modifying its value immediately affects the actual memory storage for the variable. The compiler cannot optimize away any references made to the variable. This guarantees that when one thread modifies the variable, all other threads see the new value immediately. (This is not guaranteed for non-volatile variables.)

将变量声明为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.

声明一个原子变量可以保证对该变量进行的操作以原子的方式进行,例如。该操作的所有子步骤都在执行的线程中完成,并且不会被其他线程中断。例如,递增和测试操作需要对变量进行递增,然后与另一个值进行比较;原子操作保证这两个步骤都将完成,就好像它们是一个不可分割/不可中断的操作一样。

Synchronizing all accesses to a variable allows only a single thread at a time to access the variable, and forces all other threads to wait for that accessing thread to release its access to the variable.

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

Synchronized access is similar to atomic access, but the atomic operations are generally implemented at a lower level of programming. Also, it is entirely possible to synchronize only some accesses to a variable and allow other accesses to be unsynchronized (e.g., synchronize all writes to a variable but none of the reads from it).

同步访问类似于原子访问,但是原子操作通常在较低的编程级别实现。而且,完全可能只同步一些对变量的访问,并允许其他的访问不同步(例如,同步所有的写入到一个变量,但是没有一个读取)。

Atomicity, synchronization, and volatility are independent attributes, but are typically used in combination to enforce proper thread cooperation for accessing variables.

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

Addendum (April 2016)

附录(2016年4月)

Synchronized access to a variable is usually implemented using a monitor or semaphore. These are low-level mutex (mutual exclusion) mechanisms that allow a thread to acquire control of a variable or block of code exclusively, forcing all other threads to wait if they also attempt to acquire the same mutex. Once the owning thread releases the mutex, another thread can acquire the mutex in turn.

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

Addendum (July 2016)

附录(2016年7月)

Synchronization occurs on an object. This means that calling a synchronized method of a class will lock the this object of the call. Static synchronized methods will lock the Class object itself.

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

Likewise, entering a synchronized block requires locking the this object of the method.

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

This means that a synchronized method (or block) can be executing in multiple threads at the same time if they are locking on different objects, but only one thread can execute a synchronized method (or block) at a time for any given single object.

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

#3


9  

volatile:

挥发性:

volatile is a keyword. volatile forces all threads to get latest value of the variable from main memory instead of cache. No locking is required to access volatile variables. All threads can access volatile variable value at same time.

挥发性是一个关键字。volatile迫使所有线程从主内存而不是缓存中获取变量的最新值。访问易失性变量不需要锁定。所有线程都可以同时访问可变值。

Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable.

使用volatile变量可以降低内存一致性错误的风险,因为对volatile变量的任何写入都与该变量的后续读取建立了一个事件之前的关系。

This means that changes to a volatile variable are always visible to other threads. What's more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.

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

When to use: One thread modifies the data and other threads have to read latest value of data. Other threads will take some action but they won't update data.

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

AtomicXXX:

AtomicXXX:

AtomicXXX classes support lock-free thread-safe programming on single variables. These AtomicXXX classes (like AtomicInteger) resolves memory inconsistency errors / side effects of modification of volatile variables, which have been accessed in multiple threads.

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

When to use: Multiple threads can read and modify data.

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

synchronized:

同步:

synchronized is keyword used to guard a method or code block. By making method as synchronized has two effects:

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

  1. 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.

    首先,不可能在同一对象上对同步方法进行两次调用。当一个线程正在为一个对象执行一个同步方法时,所有其他的线程都调用同一个对象块的同步方法(暂停执行),直到第一个线程对该对象完成为止。

  2. 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.

    其次,当同步方法退出时,它会自动建立与对同一对象的同步方法的任何后续调用的event -before关系。这保证对对象状态的更改对所有线程都是可见的。

When to use: Multiple threads can read and modify data. Your business logic not only update the data but also executes atomic operations

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

AtomicXXX is equivalent of volatile + synchronized even though the implementation is different. AmtomicXXX extends volatile variables + compareAndSet methods but does not use synchronization.

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

Related SE questions:

SE相关问题:

Difference between volatile and synchronized in Java

volatile和synchronized在Java中的区别

Volatile boolean vs AtomicBoolean

不稳定的布尔vs AtomicBoolean

Good articles to read: ( Above content is taken from these documentation pages)

要阅读的好文章:(以上内容取自这些文档页)

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

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/tutorial/essential/concurrency/atomic.html

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

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

#4


5  

I know that two threads can not enter in Synchronize block at the same time

我知道两个线程不能同时进入同步块。

Two thread cannot enter a synchronized block on the same object twice. This means that two threads can enter the same block on different objects. This confusion can lead to code like this.

两个线程不能在同一个对象上两次输入同步块。这意味着两个线程可以在不同的对象上输入相同的块。这种混乱可能导致这样的代码。

private Integer i = 0;

synchronized(i) {
   i++;
}

This will not behave as expected as it could be locking on a different object each time.

这将不会像预期的那样运行,因为每次都可能锁定不同的对象。

if this is true than How this atomic.incrementAndGet() works without Synchronize ?? and is thread safe ??

如果这是真的,那么这个atomic.incrementAndGet()在没有同步的情况下是如何工作的?线程安全吗?

yes. It doesn't use locking to achieve thread safety.

是的。它不使用锁定来实现线程安全。

If you want to know how they work in more detail, you can read the code for them.

如果您想更详细地了解它们是如何工作的,您可以为它们阅读代码。

And what is difference between internal reading and writing to Volatile Variable / Atomic Variable ??

对挥发性变量/原子变量的内部读写有什么区别?

Atomic class uses volatile fields. There is no difference in the field. The difference is the operations performed. The Atomic classes use CompareAndSwap or CAS operations.

原子类使用易失性字段。这一领域没有什么不同。不同之处在于执行的操作。原子类使用CompareAndSwap或CAS操作。

i read in some article that thread has local copy of variables what is that ??

我在某篇文章中读到线程有变量的局部拷贝那是什么?

I can only assume that it referring to the fact that each CPU has its own cached view of memory which can be different from every other CPU. To ensure that your CPU has a consistent view of data, you need to use thread safety techniques.

我只能假设它指的是每个CPU都有一个缓存的内存视图,这个视图可以与其他CPU不同。为了确保CPU具有一致的数据视图,您需要使用线程安全技术。

This is only an issue when memory is shared at least one thread updates it.

只有当至少有一个线程更新内存时,这才会成为一个问题。

#5


1  

A volatile + synchronization is a fool proof solution for an operation(statement) to be fully atomic which includes multiple instructions to the CPU.

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 int i = 2;i++ + i = i+ 1;这使i在执行此语句后成为内存中的值3。这包括从内存中读取i(即2)的现有值,将其加载到CPU累加器寄存器中,并通过使用1 (2 + 1 = 3 in累加器)递增现有值,然后将该递增值回内存中。虽然i的值是不稳定的,但是这些操作还不够原子化。我是volatile,只保证从内存中进行一次读/写是原子性的,而不是多次读/写。因此,我们需要在i++前后同步,以使它成为傻瓜证明的原子语句。记住一个语句包含多个语句。

Hope the explanation is clear enough.

希望这个解释足够清楚。

#6


0  

The Java volatile modifier is an example of a special mechanism to guarantee that communication happens between threads. When one thread writes to a volatile variable, and another thread sees that write, the first thread is telling the second about all of the contents of memory up until it performed the write to that volatile variable.

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

Atomic operations are performed in a single unit of task without interference from other operations. Atomic operations are necessity in multi-threaded environment to avoid data inconsistency.

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

#1


307  

You are specifically asking about how they internally work, so here you are:

你问的是他们内部是如何运作的,所以你在这里:

No synchronization

private int counter;

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

It basically reads value from memory, increments it and puts back to memory. This works in single thread but nowadays, in the era of multi-core, multi-CPU, multi-level caches it won't work correctly. First of all it introduces race condition (several threads can read the value at the same time), but also visibility problems. The value might only be stored in "local" CPU memory (some cache) and not be visible for other CPUs/cores (and thus - threads). This is why many refer to local copy of a variable in a thread. It is very unsafe. Consider this popular but broken thread-stopping code:

它基本上是从内存中读取值,然后递增,然后返回到内存中。这可以在单线程中工作,但是现在,在多核、多cpu、多级别缓存的时代,它将无法正常工作。首先,它引入了竞争条件(多个线程可以同时读取值),同时还引入了可见性问题。该值可能只存储在“本地”CPU内存(某些缓存)中,而其他CPU /内核(以及线程)不可见。这就是为什么许多线程引用变量的本地拷贝。这是非常不安全的。考虑一下这种流行但破坏了的线程停止代码:

private boolean stopped;

public void run() {
    while(!stopped) {
        //do some work
    }
}

public void pleaseStop() {
    stopped = true;
}

Add volatile to stopped variable and it works fine - if any other thread modifies stopped variable via pleaseStop() method, you are guaranteed to see that change immediately in working thread's while(!stopped) loop. BTW this is not a good way to interrupt a thread either, see: How to stop a thread that is running forever without any use and Stopping a specific java thread.

将volatile添加到已停止的变量,它可以正常工作——如果任何其他线程通过pleaseStop()方法修改已停止的变量,您可以保证在工作线程的while(!stop)循环中立即看到该更改。顺便说一下,这并不是中断线程的好方法,请参见:如何停止不使用任何使用并停止特定java线程的线程。

AtomicInteger

private AtomicInteger counter = new AtomicInteger();

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

The AtomicInteger class uses CAS (compare-and-swap) low-level CPU operations (no synchronization needed!) They allow you to modify a particular variable only if the present value is equal to something else (and is returned successfully). So when you execute getAndIncrement() it actually runs in a loop (simplified real implementation):

AtomicInteger类使用CAS(比较和交换)低级CPU操作(不需要同步!)它们只允许在现值等于其他值(并成功返回)的情况下修改特定的变量。因此,当您执行getAndIncrement()时,它实际上在一个循环中运行(简化的实际实现):

int current;
do {
  current = get();
} while(!compareAndSet(current, current + 1));

So basically: read; try to store incremented value; if not successful (the value is no longer equal to current), read and try again. The compareAndSet() is implemented in native code (assembly).

所以基本上:阅读;尝试存储递增值;如果不成功(值不再等于当前值),请再次读取并重试。compareAndSet()是在本地代码(assembly)中实现的。

volatile without synchronization

private volatile int counter;

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

This code is not correct. It fixes the visibility issue (volatile makes sure other threads can see change made to counter) but still has a race condition. This has been explained multiple times: pre/post-incrementation is not atomic.

这段代码不正确。它修复了可见性问题(volatile确保其他线程可以看到为计数器而做的更改),但仍然存在竞争条件。这已经多次解释了:pre/后增量不是原子的。

The only side effect of volatile is "flushing" caches so that all other parties see the freshest version of the data. This is too strict in most situations; that is why volatile is not default.

volatile的唯一副作用是“刷新”缓存,这样所有其他方都可以看到数据的最新版本。这在大多数情况下都太严格了;这就是为什么volatile不会出现违约。

volatile without synchronization (2)

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

The same problem as above, but even worse because i is not private. The race condition is still present. Why is it a problem? If, say, two threads run this code simultaneously, the output might be + 5 or + 10. However, you are guaranteed to see the change.

和上面的问题一样,但更糟糕的是,因为我不是私人。种族状况仍然存在。为什么这是个问题?如果两个线程同时运行此代码,输出可能是+ 5或+ 10。但是,您肯定会看到更改。

Multiple independent synchronized

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

Surprise, this code is incorrect as well. In fact, it is completely wrong. First of all you are synchronizing on i, which is about to be changed (moreover, i is a primitive, so I guess you are synchronizing on a temporary Integer created via autoboxing...) Completely flawed. You could also write:

令人惊讶的是,这个代码也不正确。事实上,这是完全错误的。首先,您正在对i进行同步,它将被更改(而且,i是一个原语,所以我猜您正在对通过自动装箱创建的临时整数进行同步…)完全有缺陷的。你也可以写:

synchronized(new Object()) {
  //thread-safe, SRSLy?
}

No two threads can enter the same synchronized block with the same lock. In this case (and similarly in your code) the lock object changes upon every execution, so synchronized effectively has no effect.

没有两个线程可以使用相同的锁进入同一个同步块。在这种情况下(在您的代码中也是如此),锁对象在每次执行时都会发生变化,因此有效地同步没有效果。

Even if you have used a final variable (or this) for synchronization, the code is still incorrect. Two threads can first read i to temp synchronously (having the same value locally in temp), then the first assigns a new value to i (say, from 1 to 6) and the other one does the same thing (from 1 to 6).

即使您使用了一个最终变量(或这个)进行同步,代码仍然是不正确的。两个线程可以首先同步地将i读到temp(在temp中具有相同的本地值),然后第一个线程为i分配一个新值(例如,从1到6),另一个线程执行相同的操作(从1到6)。

The synchronization must span from reading to assigning a value. Your first synchronization has no effect (reading an int is atomic) and the second as well. In my opinion, these are the correct forms:

同步必须从读取扩展到分配值。第一个同步没有效果(读取int是原子的),第二个同步也一样。在我看来,这些是正确的形式:

void synchronized incIBy5() {
  i += 5 
}

void incIBy5() {
  synchronized(this) {
    i += 5 
  }
}

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

#2


38  

Declaring a variable as volatile means that modifying its value immediately affects the actual memory storage for the variable. The compiler cannot optimize away any references made to the variable. This guarantees that when one thread modifies the variable, all other threads see the new value immediately. (This is not guaranteed for non-volatile variables.)

将变量声明为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.

声明一个原子变量可以保证对该变量进行的操作以原子的方式进行,例如。该操作的所有子步骤都在执行的线程中完成,并且不会被其他线程中断。例如,递增和测试操作需要对变量进行递增,然后与另一个值进行比较;原子操作保证这两个步骤都将完成,就好像它们是一个不可分割/不可中断的操作一样。

Synchronizing all accesses to a variable allows only a single thread at a time to access the variable, and forces all other threads to wait for that accessing thread to release its access to the variable.

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

Synchronized access is similar to atomic access, but the atomic operations are generally implemented at a lower level of programming. Also, it is entirely possible to synchronize only some accesses to a variable and allow other accesses to be unsynchronized (e.g., synchronize all writes to a variable but none of the reads from it).

同步访问类似于原子访问,但是原子操作通常在较低的编程级别实现。而且,完全可能只同步一些对变量的访问,并允许其他的访问不同步(例如,同步所有的写入到一个变量,但是没有一个读取)。

Atomicity, synchronization, and volatility are independent attributes, but are typically used in combination to enforce proper thread cooperation for accessing variables.

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

Addendum (April 2016)

附录(2016年4月)

Synchronized access to a variable is usually implemented using a monitor or semaphore. These are low-level mutex (mutual exclusion) mechanisms that allow a thread to acquire control of a variable or block of code exclusively, forcing all other threads to wait if they also attempt to acquire the same mutex. Once the owning thread releases the mutex, another thread can acquire the mutex in turn.

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

Addendum (July 2016)

附录(2016年7月)

Synchronization occurs on an object. This means that calling a synchronized method of a class will lock the this object of the call. Static synchronized methods will lock the Class object itself.

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

Likewise, entering a synchronized block requires locking the this object of the method.

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

This means that a synchronized method (or block) can be executing in multiple threads at the same time if they are locking on different objects, but only one thread can execute a synchronized method (or block) at a time for any given single object.

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

#3


9  

volatile:

挥发性:

volatile is a keyword. volatile forces all threads to get latest value of the variable from main memory instead of cache. No locking is required to access volatile variables. All threads can access volatile variable value at same time.

挥发性是一个关键字。volatile迫使所有线程从主内存而不是缓存中获取变量的最新值。访问易失性变量不需要锁定。所有线程都可以同时访问可变值。

Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable.

使用volatile变量可以降低内存一致性错误的风险,因为对volatile变量的任何写入都与该变量的后续读取建立了一个事件之前的关系。

This means that changes to a volatile variable are always visible to other threads. What's more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.

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

When to use: One thread modifies the data and other threads have to read latest value of data. Other threads will take some action but they won't update data.

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

AtomicXXX:

AtomicXXX:

AtomicXXX classes support lock-free thread-safe programming on single variables. These AtomicXXX classes (like AtomicInteger) resolves memory inconsistency errors / side effects of modification of volatile variables, which have been accessed in multiple threads.

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

When to use: Multiple threads can read and modify data.

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

synchronized:

同步:

synchronized is keyword used to guard a method or code block. By making method as synchronized has two effects:

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

  1. 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.

    首先,不可能在同一对象上对同步方法进行两次调用。当一个线程正在为一个对象执行一个同步方法时,所有其他的线程都调用同一个对象块的同步方法(暂停执行),直到第一个线程对该对象完成为止。

  2. 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.

    其次,当同步方法退出时,它会自动建立与对同一对象的同步方法的任何后续调用的event -before关系。这保证对对象状态的更改对所有线程都是可见的。

When to use: Multiple threads can read and modify data. Your business logic not only update the data but also executes atomic operations

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

AtomicXXX is equivalent of volatile + synchronized even though the implementation is different. AmtomicXXX extends volatile variables + compareAndSet methods but does not use synchronization.

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

Related SE questions:

SE相关问题:

Difference between volatile and synchronized in Java

volatile和synchronized在Java中的区别

Volatile boolean vs AtomicBoolean

不稳定的布尔vs AtomicBoolean

Good articles to read: ( Above content is taken from these documentation pages)

要阅读的好文章:(以上内容取自这些文档页)

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

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/tutorial/essential/concurrency/atomic.html

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

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

#4


5  

I know that two threads can not enter in Synchronize block at the same time

我知道两个线程不能同时进入同步块。

Two thread cannot enter a synchronized block on the same object twice. This means that two threads can enter the same block on different objects. This confusion can lead to code like this.

两个线程不能在同一个对象上两次输入同步块。这意味着两个线程可以在不同的对象上输入相同的块。这种混乱可能导致这样的代码。

private Integer i = 0;

synchronized(i) {
   i++;
}

This will not behave as expected as it could be locking on a different object each time.

这将不会像预期的那样运行,因为每次都可能锁定不同的对象。

if this is true than How this atomic.incrementAndGet() works without Synchronize ?? and is thread safe ??

如果这是真的,那么这个atomic.incrementAndGet()在没有同步的情况下是如何工作的?线程安全吗?

yes. It doesn't use locking to achieve thread safety.

是的。它不使用锁定来实现线程安全。

If you want to know how they work in more detail, you can read the code for them.

如果您想更详细地了解它们是如何工作的,您可以为它们阅读代码。

And what is difference between internal reading and writing to Volatile Variable / Atomic Variable ??

对挥发性变量/原子变量的内部读写有什么区别?

Atomic class uses volatile fields. There is no difference in the field. The difference is the operations performed. The Atomic classes use CompareAndSwap or CAS operations.

原子类使用易失性字段。这一领域没有什么不同。不同之处在于执行的操作。原子类使用CompareAndSwap或CAS操作。

i read in some article that thread has local copy of variables what is that ??

我在某篇文章中读到线程有变量的局部拷贝那是什么?

I can only assume that it referring to the fact that each CPU has its own cached view of memory which can be different from every other CPU. To ensure that your CPU has a consistent view of data, you need to use thread safety techniques.

我只能假设它指的是每个CPU都有一个缓存的内存视图,这个视图可以与其他CPU不同。为了确保CPU具有一致的数据视图,您需要使用线程安全技术。

This is only an issue when memory is shared at least one thread updates it.

只有当至少有一个线程更新内存时,这才会成为一个问题。

#5


1  

A volatile + synchronization is a fool proof solution for an operation(statement) to be fully atomic which includes multiple instructions to the CPU.

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 int i = 2;i++ + i = i+ 1;这使i在执行此语句后成为内存中的值3。这包括从内存中读取i(即2)的现有值,将其加载到CPU累加器寄存器中,并通过使用1 (2 + 1 = 3 in累加器)递增现有值,然后将该递增值回内存中。虽然i的值是不稳定的,但是这些操作还不够原子化。我是volatile,只保证从内存中进行一次读/写是原子性的,而不是多次读/写。因此,我们需要在i++前后同步,以使它成为傻瓜证明的原子语句。记住一个语句包含多个语句。

Hope the explanation is clear enough.

希望这个解释足够清楚。

#6


0  

The Java volatile modifier is an example of a special mechanism to guarantee that communication happens between threads. When one thread writes to a volatile variable, and another thread sees that write, the first thread is telling the second about all of the contents of memory up until it performed the write to that volatile variable.

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

Atomic operations are performed in a single unit of task without interference from other operations. Atomic operations are necessity in multi-threaded environment to avoid data inconsistency.

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