当前位置: 首页 > 面试题库 >

Javaatomic / volatile / synchronized有什么区别?

谢骏奇
2023-03-14
问题内容

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

以下代码块有什么区别?

代码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 }
}

我认为两个线程不能同时进入同步块…对吗?如果这是真的,那么atomic.incrementAndGet()没有它synchronized怎么办?而且它是线程安全的吗?

内部读写可变变量/原子变量之间有什么区别?我在某些文章中读到,线程具有变量的本地副本-那是什么?


问题答案:

你是在专门询问它们在内部如何工作,所以你在这里:

No synchronization

private int counter;

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

它基本上是从内存中读取值,然后将其递增并放回内存中。它可以在单线程中运行,但在当今的多核,多CPU,多级缓存时代,它无法正常工作。首先,它引入了竞争条件(多个线程可以同时读取值),还引入了可见性问题。该值可能仅存储在“ 本地 ” CPU内存(某些缓存)中,而其他CPU /内核(因此-线程)不可见。这就是为什么许多人在线程中引用变量的本地副本的原因。这是非常不安全的。考虑以下流行但破损的线程停止代码:

private boolean stopped;

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

public void pleaseStop() {
    stopped = true;
}

添加volatilestopped变量,它可以正常工作-如果任何其他线程stopped通过pleaseStop()方法修改了变量,则可以保证在工作线程的while(!stopped)循环中立即看到该更改。顺便说一句,这也不是中断线程的好方法,请参阅:如何停止永远不运行的线程以及停止特定的Java线程。

AtomicInteger

private AtomicInteger counter = new AtomicInteger();

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

AtomicInteger类使用CAS(比较并交换)低级别的CPU操作(没有同步需要!)他们允许你修改某个变量只有当现值等于其他的东西(并成功返回)。因此,当你执行getAndIncrement()它时,它实际上是在循环中运行(简化的实际实现):

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

所以基本上:阅读;尝试存储增加的值;如果不成功(该值不再等于current),请阅读并重试。的compareAndSet()是在本机代码(组件)来实现。

volatile without synchronization

private volatile int counter;

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

该代码不正确。它解决了可见性问题(volatile确保其他线程可以看到对所做的更改counter),但仍然存在竞争状况。这已被多次解释:递增前/递后不是原子的。

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

volatile without synchronization (2)

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

与上述相同的问题,但更糟糕的i是,不是private。竞赛条件仍然存在。为什么会出问题呢?例如,如果两个线程同时运行此代码,则输出可能为+ 5或+ 10。但是,你一定可以看到更改。

Multiple independent synchronized

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

令人惊讶的是,此代码也不正确。实际上,这是完全错误的。首先,你要在上进行同步i,该操作将被更改(此外,i它是原始操作,因此我想你正在Integer通过自动装箱创建的临时文件上进行同步…)完全有缺陷。你还可以编写:

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

没有两个线程可以使用相同的锁进入同synchronized一块。在这种情况下(以及类似地在你的代码中),锁定对象在每次执行时都会更改,因此实际上无效。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;
  }
}


 类似资料:
  • 本文向大家介绍synchronized 和 Lock 有什么区别?相关面试题,主要包含被问及synchronized 和 Lock 有什么区别?时的应答技巧和注意事项,需要的朋友参考一下 synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。 synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁

  • 问题内容: 我想知道使用ReentrentLock和Synchronized(object)有什么大不同吗? 为什么叫reentrentLock?允许来自同一线程的递归调用? 问题答案: 我想知道使用和会有很大的不同。 主要区别在于: 与锁定/解锁绑定到源代码的块结构。无论您如何执行此操作,退出块时都会释放一个锁。例如,如果块由于意外的异常而终止,它将被释放。 使用显式锁定不是这种情况,因此您可以

  • 问题内容: 我想知道将变量声明为和始终在Java块中访问变量之间的区别吗? 问题答案: 重要的是要了解线程安全有两个方面。 execution control, and memory visibility 第一个与控制代码何时执行(包括执行指令的顺序)以及是否可以同时执行有关,第二个与其他线程可以看到存储器中已完成操作的效果有关。由于每个CPU与主内存之间都具有多个高速缓存级别,因此运行在不同CP

  • 本文向大家介绍java中volatile和synchronized的区别与联系,包括了java中volatile和synchronized的区别与联系的使用技巧和注意事项,需要的朋友参考一下 java中volatile和synchronized的区别与联系 这个可能是最好的对比volatile和synchronized作用的文章了。volatile是一个变量修饰符,而synchronized是一个

  • 本文向大家介绍详解java并发编程(2) --Synchronized与Volatile区别,包括了详解java并发编程(2) --Synchronized与Volatile区别的使用技巧和注意事项,需要的朋友参考一下 1 Synchronized 在多线程并发中synchronized一直是元老级别的角色。利用synchronized来实现同步具体有一下三种表现形式: 对于普通的同步方法,锁是当

  • 问题内容: 今天的工作中,我遇到了volatileJava中的关键字。不太熟悉,我找到了这种解释。 鉴于该文章详细解释了所讨论的关键字,您是否曾经使用过它,或者是否曾见过可以正确使用此关键字的情况? 问题答案: volatile具有内存可见性的语义。基本上,volatile字段的值对所有读取器(尤其是其他线程)可见,在该字段上完成写操作之后。没有volatile,读者可能会看到一些未更新的值。 要