Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/363.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 原子/易失性/同步之间有什么区别?_Java_Multithreading_Synchronization_Atomic_Volatile - Fatal编程技术网

Java 原子/易失性/同步之间有什么区别?

Java 原子/易失性/同步之间有什么区别?,java,multithreading,synchronization,atomic,volatile,Java,Multithreading,Synchronization,Atomic,Volatile,原子/易失性/同步化如何在内部工作 以下代码块之间的区别是什么 代码1 private int counter; public int getNextUniqueIndex() { return counter++; } 代码2 private AtomicInteger counter; public int getNextUniqueIndex() { return counter.getAndIncrement(); } 代码3 private volatile

原子/易失性/同步化如何在内部工作

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

代码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
是否按以下方式工作?是

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

Integer i = 5;
void incIBy5() {
    int temp;
    synchronized(i) { temp = i }
    synchronized(i) { i = temp + 5 }
}
我认为两个线程不能同时进入一个同步块。。。我说得对吗?如果这是真的,
原子.incrementAndGet()
在没有
同步的情况下如何工作?它是线程安全的吗


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

您特别询问了变量的内部工作方式,因此您可以:

不同步 它基本上从内存中读取值,增加值,然后放回内存。它可以在单线程中工作,但在当今多核、多CPU、多级缓存的时代,它无法正常工作。首先,它引入了竞争条件(多个线程可以同时读取值),但也存在可见性问题。该值可能只存储在“本地”CPU内存(一些缓存)中,对于其他CPU/核心(以及线程)不可见。这就是为什么许多引用线程中变量的本地副本。这是非常不安全的。考虑这个流行但中断的线程停止代码:

private boolean stopped;

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

public void pleaseStop() {
    stopped = true;
}
volatile
添加到
stopped
变量中,它工作正常-如果任何其他线程通过
plesestop()
方法修改
stopped
变量,则可以保证在工作线程的
while(!stopped)
循环中立即看到该更改。顺便说一句,这也不是中断线程的好方法,请参见:和

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

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

volatile
不同步 此代码不正确。它修复了可见性问题(
volatile
确保其他线程可以看到对
计数器所做的更改),但仍然存在争用条件。这已经发生了多次:增量前后不是原子的

volatile
的唯一副作用是“刷新”缓存,以便所有其他方都能看到最新版本的数据。这在大多数情况下过于严格;这就是为什么
volatile
不是默认值

volatile
不同步(2) 与上述问题相同,但更糟糕的是,
i
不是
private
。比赛条件仍然存在。为什么这是个问题?例如,如果两个线程同时运行此代码,则输出可能是
+5
+10
。但是,您肯定会看到变化

多个独立的
同步的

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

synchronized(new Object()) {
  //thread-safe, SRSLy?
}
没有两个线程可以使用相同的锁进入相同的
同步
。在这种情况下(在代码中也是如此),锁对象在每次执行时都会更改,因此
synchronized
实际上没有任何效果

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

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

void synchronized incIBy5() {
  i += 5 
}

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

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

您特别询问了它们的内部工作方式,因此您可以:

不同步 它基本上从内存中读取值,增加值,然后放回内存。它可以在单线程中工作,但在当今多核、多CPU、多级缓存的时代,它无法正常工作。首先,它引入了竞争条件(多个线程可以同时读取值),但也存在可见性问题。该值可能只存储在“本地”CPU内存(一些缓存)中,对于其他CPU/核心(以及线程)不可见。这就是为什么许多引用线程中变量的本地副本。这是非常不安全的。考虑这个流行但中断的线程停止代码:

private boolean stopped;

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

public void pleaseStop() {
    stopped = true;
}
volatile
添加到
stopped
变量中,它工作正常-如果任何其他线程通过
plesestop()
方法修改
stopped
变量,则可以保证在工作线程的
while(!stopped)
循环中立即看到该更改。顺便说一句,这也不是中断线程的好方法,请参见:和

AtomicInteger
AtomicInteger
类使用CAS()低级CPU操作(不需要同步!),它们允许您仅在当前值等于其他值(并且成功返回)时修改特定变量。因此,当您执行
getAndIncrement()
时,它实际上在一个循环中运行(简化的实际实现):
private volatile int counter;

public int getNextUniqueIndex() {
  return counter++; 
}
volatile int i = 0;
void incIBy5() {
  i += 5;
}
void incIBy5() {
  int temp;
  synchronized(i) { temp = i }
  synchronized(i) { i = temp + 5 }
}
synchronized(new Object()) {
  //thread-safe, SRSLy?
}
void synchronized incIBy5() {
  i += 5 
}

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

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

synchronized(i) {
   i++;
}