Java 为什么volatile和synchronized语句在这里不能避免线程干扰?

Java 为什么volatile和synchronized语句在这里不能避免线程干扰?,java,multithreading,concurrency,Java,Multithreading,Concurrency,如果我更改为synchronized语句,线程干扰也会发生: 1000 1230 为什么?我相信发生的是: count++尝试递增一个不可变的整数。编译器必须创建一个新的整数并将其称为“count”。前一个计数上的同步锁将异常运行。一个非常聪明的人可能会解决这个问题 将count更改为一个基元int,并在其他对象上同步以查看发生了什么。或者使用原子整数 主要的收获是不要增加整数!除非您确定。而不是同步计数 尝试执行自身已同步的递增/递减方法 1000 1008 并调用它们,而不是直接递减/递

如果我更改为
synchronized
语句,线程干扰也会发生:

1000
1230

为什么?

我相信发生的是:

count++
尝试递增一个不可变的
整数。编译器必须创建一个新的整数并将其称为“count”。前一个计数上的同步锁将异常运行。一个非常聪明的人可能会解决这个问题

将count更改为一个基元
int
,并在其他对象上同步以查看发生了什么。或者使用原子整数


主要的收获是不要增加整数!除非您确定。

而不是同步计数

尝试执行自身已同步的递增/递减方法

1000
1008
并调用它们,而不是直接递减/递增

此外,如果您只执行一个计数器,而不使用同步来防止线程干扰,则应该使用原子值


无论如何,检查一下这个演示如何使用同步方法或原子值进行同步计数器的文档:

当您这样做时,您是做错了

public static synchronized decrementCount()
{
    count--;
}

public static synchronized incrementCount()
{
    count++;
}
公共静态易失性整数计数=1000;
对于(int i=0;i<10000;i++){
已同步(计数){
计数++;
计数--;
计数++;
计数--;
}
}
整数是不可变的。这意味着,当您执行
count++
操作时,它会创建一个新的
Integer
对象,并使用该新对象分配
count
。同时,同步部分仍然引用旧对象。因此,当每个线程到达具有不同对象的同步块时,存在一个机会。
您应该使用在进程发生时不指向不同对象的变量

正如其他人所指出的,您有两个不同的问题。第一个是
count++
(和
count--
)不是原子操作,即使对于基本
int
类型也是如此。因此,如果没有某种锁定或其他并发处理,这将无法工作。对于
count++
,其中count是一个
int
,编译器生成如下字节码指令:

        public static volatile Integer count = 1000;
        for (int i = 0; i < 10000; i++) {
            synchronized(count) {
                count++;
                count--;
                count++;
                count--;
            }
        }
即使编译为本机代码,这也不太可能是原子的

第二个问题是您没有锁定一致的对象,因此操作没有序列化。守则:

    getstatic count
    iconst_1
    iadd
    putstatic count
创建一个新对象。从本质上讲,它的作用如下:

   count++; // "count" is an "Integer" object type.
因此,您的
count
对象将被一个新对象替换,随后进入
synchronized
部分的条目将针对另一个对象进行同步

作为安全提示,如果您使用的是
synchronized(someObject)
,其中
someObject
是一个类或实例字段,那么最好将该字段设置为最终字段
。这样就不会无意中将其重新分配到其他值

对于这个问题,我可以想到两个简单的解决办法。针对特定对象锁定的一个,用于锁定,如下所示:

   count = Integer.valueOf(count.intValue() + 1);

第一个很简单--
volatile
使访问可见并有序。两个线程各自执行一个“读、修改、写”操作,这两个线程可以互相践踏。如果我将
Integer更改为int
或将
count++
更改为
count=count+1
@FANGYishu,则线程仍然会中断。您无法在基本类型上同步,因此对
int
的更改甚至无法编译。对于
count=count+1
,您基本上是在做与
count++
相同的事情,因此显然这不起作用。您仍然必须在某些方面进行同步,因为++不是原子的。顺便说一句,一个AtomicInteger也是一个不错的选择。我真的很喜欢将同步对象设置为final的建议,但其余的只是重复前面的答案。@user949300,我认为它扩展了前面的答案,但如果您有不同的看法,我可以理解。
   count++; // "count" is an "Integer" object type.
   count = Integer.valueOf(count.intValue() + 1);
public class ThreadInterference {
  public static final Object COUNT_LOCK = new Object();
  public static int count = 1000; // Either "int" or "Integer" OK, depending on need

  public static class MyThread implements Runnable {
    @Override
    public void run() {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      for (int i = 0; i < 10000; i++) {
        synchronized(COUNT_LOCK) {
          count++;
          count--;
          count++;
          count--;
        }
      }
    }
  }

  // And so on...
}
public class ThreadInterference {
  public static AtomicInteger count = new AtomicInteger(1000);

  public static class MyThread implements Runnable {
    @Override
    public void run() {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      for (int i = 0; i < 10000; i++) {
        count.incrementAndGet();
        count.decrementAndGet();
        count.incrementAndGet();
        count.decrementAndGet();
      }
    }
  }

  // And so on...
}