Java 为什么volatile plus synchronized不';不行?
我试图理解java中的并发性。我知道synchronized,它在对象上创建了一个监视器,然后其他线程就不能对这个对象进行操作了。Volatile是关于处理器缓存的,如果我使用它,所有线程都不会创建对象的副本。因此,在我看来,如果我运行这段代码,我将得到正确的计数器值(40000)。但我错了Java 为什么volatile plus synchronized不';不行?,java,concurrency,Java,Concurrency,我试图理解java中的并发性。我知道synchronized,它在对象上创建了一个监视器,然后其他线程就不能对这个对象进行操作了。Volatile是关于处理器缓存的,如果我使用它,所有线程都不会创建对象的副本。因此,在我看来,如果我运行这段代码,我将得到正确的计数器值(40000)。但我错了 public class Main { private static volatile Integer counter = 0; public static void main(String[] arg
public class Main {
private static volatile Integer counter = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Counter();
Thread thread2 = new Counter();
thread.start();
thread2.start();
thread.join();
thread2.join();
System.out.println(counter);
}
public static class Counter extends Thread{
@Override
public void run() {
for (int i = 0; i < 20000; i++) {
synchronized (counter){
counter++;
}
}
}
}
}
公共类主{
专用静态易失性整数计数器=0;
公共静态void main(字符串[]args)引发InterruptedException{
线程=新计数器();
螺纹2=新计数器();
thread.start();
thread2.start();
thread.join();
螺纹2.连接();
系统输出打印项次(计数器);
}
公共静态类计数器扩展线程{
@凌驾
公开募捐{
对于(int i=0;i<20000;i++){
已同步(计数器){
计数器++;
}
}
}
}
}
但如果我使用syncronized方法,我将得到正确的结果:
public class Main {
private static volatile Integer counter = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Counter();
Thread thread2 = new Counter();
thread.start();
thread2.start();
thread.join();
thread2.join();
System.out.println(counter);
}
public synchronized static void increment(){
counter++;
}
public static class Counter extends Thread{
@Override
public void run() {
for (int i = 0; i < 20000; i++) {
increment();
}
}
}
}
公共类主{
专用静态易失性整数计数器=0;
公共静态void main(字符串[]args)引发InterruptedException{
线程=新计数器();
螺纹2=新计数器();
thread.start();
thread2.start();
thread.join();
螺纹2.连接();
系统输出打印项次(计数器);
}
公共同步静态无效增量(){
计数器++;
}
公共静态类计数器扩展线程{
@凌驾
公开募捐{
对于(int i=0;i<20000;i++){
增量();
}
}
}
}
那个么问题-为什么synchronized对Integer对象不起作用???您在非final字段上使用了
synchronized
块。使用计数器+++
时,由于整数是不可变的,因此新的引用将分配给计数器
。请检查此答案以了解更多详细信息-
您可以使用ReentrantLock
而不是synchronized,但另一个问题是,您没有在volatile字段上使用原子操作。您应该改用AtomicInteger
导入java.util.concurrent.AtomicInteger;
公共班机{
私有静态最终AtomicInteger计数器=新的AtomicInteger(0);
公共静态void main(字符串[]args)引发InterruptedException{
线程=新计数器();
螺纹2=新计数器();
thread.start();
thread2.start();
thread.join();
螺纹2.连接();
系统输出打印项次(计数器);
}
公共静态类计数器扩展线程{
@凌驾
公开募捐{
对于(int i=0;i<20000;i++){
counter.getAndIncrement();
}
}
}
}
作为参考,使用lock时,您不需要volatile:
import java.util.concurrent.locks.Lock;
导入java.util.concurrent.locks.ReentrantLock;
公共班机{
专用静态整数计数器=0;
private static final Lock=new ReentrantLock();
公共静态void main(字符串[]args)引发InterruptedException{
线程=新计数器();
螺纹2=新计数器();
thread.start();
thread2.start();
thread.join();
螺纹2.连接();
系统输出打印项次(计数器);
}
公共静态类计数器扩展线程{
@凌驾
公开募捐{
对于(int i=0;i<20000;i++){
lock.lock();
计数器++;
lock.unlock();
}
}
}
}
您误解了几件事:
volatile
与CPU缓存无关。所有现代处理器都采用对应用程序完全透明的多级CPU缓存,因此应用程序不必关心它们是否从L1、L2、L3、RAM等获取。这是通过CPU实现一些缓存一致性协议来实现的,例如或其变体。那么,volatile
会做什么呢?它会阻止某些编译器优化。例如,如果只读取一次变量的值,而不使用volatile
,编译器可能会优化掉该变量的任何后续读取,因为它假定该变量不可能更改。使用volatile
时,它不会删除这些额外的读取
synchronized
关键字使用某个对象作为锁,但在您的情况下,您正在更改该对象。因此,假设线程1锁定整数(0),然后由于自动装箱,++操作将对象更改为整数(1)。您的第二个线程可以自由获取该锁,因为它不是由任何人持有的
因此,对于同步,最好使用适当的锁并避免使用
synchronized
字。我甚至更进一步地说,这在java中是一个错误。需要注意的关键是,Integer
是一个不可变的类型,counter++
实际上是将一个引用分配给另一个Integer
对象到counter
。然后duplink提供了答案。(事实上,计数器
也是易失的
与此无关。)“重复”回答了一个完全不同的问题。副本讲述了一个常见的新手错误:新手编写synchronized(x)
,然后将不同的对象引用分配给x
。但是这本书的作者