简体   繁体   English

同步块中的原子变量

[英]Atomic variables in synchronized blocks

I'm referring to the Java language but I think it can concern other languages as well. 我指的是Java语言,但我认为它也可以涉及其他语言。

The question is: does it make sense to put an atomic variable in a synchronized block? 问题是: 将原子变量放在同步块中是否有意义? Or the fact that the synchronized block will not be executed by two threads at the same time is enough to guarantee that the operations performed in the synchronized block are atomic? 抑或是同步块不会同时由两个线程执行,这一事实足以保证在同步块中执行的操作是原子的? (Example 1 vs. Example 2) (示例1与示例2)

Example 1: 范例1:

public class SharedVariables {
  public static AtomicInteger i;
}

public class MyThread extends Thread {
  public void run() {
    synchronized(SharedVariables.i) {
      i.getAndIncrement();
    }
  }
}

Example 2: 范例2:

public class MyIntegerWrapper {
  public int i;
  public void increment() {
    this.i++;
  }
}

public class SharedVariables {
  public static MyIntegerWrapper i;
}

public class MyThread extends Thread {
  public void run() {
    synchronized(SharedVariables.i) {
      i.increment();
    }
  }
}

Not by itself no. 本身不是。 But if you're doing an operation that involves a thread-safe class like AtomicInteger and other variables (even if they're other AtomicIntegers ), you might need to. 但是,如果您执行的操作涉及诸如AtomicInteger类的线程安全类和其他变量(即使它们是其他AtomicIntegers ),则可能需AtomicIntegers

AtomicInteger guarantees that operations on it are thread-safe and atomic without you needing to do anything. AtomicInteger保证对它的操作是线程安全的和原子的,而您无需执行任何操作。 But let's say that you increment one and decrement another, and their sum always needs to be the same. 但是,假设您增加一个,然后减少另一个,它们的总和必须始终相同。 You have 2 atomic operations, but to combine them into a single atomic operation you'll need additional synchronization. 您有2个原子操作,但是要将它们组合为一个原子操作,则需要额外的同步。 Only then can you be sure that other threads will read a constant sum of those variables. 只有这样,您才能确保其他线程将读取这些变量的常数。

I'd still be careful when combining manual synchronization with java.util.concurrent classes, as there's most likely a far cleaner solution provided by the concurrent classes. 在将手动同步与java.util.concurrent类结合使用时,我仍然要小心,因为并发类很可能提供了一种更为简洁的解决方案。

synchronized is not required in your first code, the guarantees of atomic variables are sufficient. 在您的第一个代码中并不需要synchronized ,因此原子变量的保证就足够了。

Your second code has one issue: the int variable is public, so you can't guarantee that it will always be called from a synchronized block. 您的第二个代码有一个问题: int变量是公共变量,因此您不能保证始终从synchronized块中调用它。

原子变量使用非阻塞算法(它使用比较和交换机制),因此您不应在同步块内使用它,而应尝试尽可能使用原子变量以获得更好的性能。

In your examples its totally ok to use either AtomicInteger without synchronized or your MyIntegerWrapper inside synchronized . 在您的示例中,完全可以使用不synchronized AtomicInteger或其中的MyIntegerWrapper synchronized

But in general case it depends on your variable visibility scope. 但通常情况下,这取决于您的可变可见性范围。 For example if you decided to use MyIntegerWrapper instead of AtomicInteger , then you must ensure that there is no other method which uses it without synchronization. 例如,如果您决定使用MyIntegerWrapper而不是AtomicInteger ,则必须确保没有其他方法可以使用它而不进行同步。

Also what to choose depends on how often is the variable modified by different threads. 同样选择什么取决于变量被不同线程修改的频率。 AtomoicInteger uses compare-and-set (or just cas) operation inside instead of synchronization. AtomoicInteger使用比较并设置 (或只是cas)操作而不是同步。 CAS is believed to be faster as there is less overhead comparing to entering synchronized block, however in a high contention case(when there lots of threads trying to modify this variable at the same time) synchronized block can be more efficient. 与进入synchronized块相比,CAS具有更快的开销,因为它具有较少的开销,但是在争用较高的情况下(当有很多线程试图同时修改此变量时), synchronized块会更有效。 Also synchronized gives jvm a space for some optimizations, like lock coarsering. synchronized为jvm提供了一些优化的空间,例如锁定粗化。 So just keep that in mind when choosing AtomicInteger over synchronized . 所以在选购时只需记住这一点AtomicIntegersynchronized

In first example you use two synchronizes. 在第一个示例中,您使用两个同步。

AtomicInteger implements " Non-blocking synchronization algorithm " AtomicInteger实现“ 无阻塞同步算法

Exactly Look free algorithm that allows individual threads to starve but guarantees system-wide throughput. 完全免费的算法,允许单个线程挨饿,但保证了系统范围的吞吐量。

This algorithm works with helping of machine instructions. 该算法可在机器指令的帮助下工作。 For example in the x86 (since 80486) and Itanium architectures this is implemented as the compare and exchange (CMPXCHG) instruction. 例如,在x86(自80486起)和Itanium体系结构中,将其实现为比较和交换(CMPXCHG)指令。

Your key word "synchronized" is redundant and even does not allow to work Lock free algorithm which gives good advantages. 您的关键字“同步”是多余的,甚至不允许使用无算法,该算法具有很好的优势。

Therefore, in your case don`t use synchronized block, use AtomicInteger. 因此,在您不使用同步块的情况下,请使用AtomicInteger。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM