我指的是Java语言,但我认为它也可以涉及其他语言。
问题是:将原子变量放在同步块中是否有意义?或者同步块不会同时由两个线程执行的事实足以保证在synchronized块中执行的操作是原子的吗? (实施例1与实施例2)
示例1:
public class SharedVariables {
public static AtomicInteger i;
}
public class MyThread extends Thread {
public void run() {
synchronized(SharedVariables.i) {
i.getAndIncrement();
}
}
}
示例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();
}
}
}
答案 0 :(得分:2)
不是自己没有。但是,如果您正在执行涉及线程安全类(如AtomicInteger
)和其他变量(即使它们是其他AtomicIntegers
)的操作,您可能需要这样做。
AtomicInteger
保证对它的操作是线程安全的和原子的,而不需要做任何事情。但是,让我们说你增加一个并递减另一个,它们的总和总是需要相同。您有2个原子操作,但要将它们组合成单个原子操作,您需要进行额外的同步。只有这样,你才能确定其他线程会读取这些变量的常数总和。
在将手动同步与java.util.concurrent
类结合使用时,我仍然要小心,因为并发类提供的解决方案最可能是更清晰的解决方案。
答案 1 :(得分:0)
synchronized
在您的第一个代码中不是必需的,原子变量的保证就足够了。
您的第二个代码有一个问题:int
变量是公开的,因此您无法保证始终从synchronized
块调用它。
答案 2 :(得分:0)
原子变量使用非阻塞算法(它使用比较和交换机制),所以你不应该在synchronized块中使用它,而是尝试尽可能使用原子变量以获得更好的性能。
答案 3 :(得分:0)
在您的示例中,完全可以使用AtomicInteger
而不是synchronized
或MyIntegerWrapper
在synchronized
内。
但一般情况下,它取决于您的可变可见范围。例如,如果您决定使用MyIntegerWrapper
而不是AtomicInteger
,则必须确保没有其他方法在没有同步的情况下使用它。
另外选择什么取决于不同线程修改变量的频率。 AtomoicInteger
使用 compare-and-set (或只是cas)操作而不是同步。 CAS被认为更快,因为与输入synchronized
块相比,开销更少,但是在高争用情况下(当有许多线程试图同时修改此变量时)synchronized
块可以更有效率。同样synchronized
为jvm提供了一些优化空间,比如锁定粗化。因此,选择AtomicInteger
而不是synchronized
时,请记住这一点。
答案 4 :(得分:0)
在第一个示例中,您使用两个同步。
AtomicInteger实现" Non-blocking synchronization algorithm"
完全看起来免费算法,允许单个线程饿死但保证系统范围的吞吐量。
此算法适用于机器指令。例如,在x86(自80486)和Itanium体系结构中,这是作为比较和交换(CMPXCHG)指令实现的。
你的关键词"同步"是多余的,甚至不允许工作无锁算法,这提供了很好的优势。
因此,在您的情况下,不要使用synchronized块,请使用AtomicInteger。