Java 从多个线程进行不安全访问,将单个值设置为单个变量

Java 从多个线程进行不安全访问,将单个值设置为单个变量,java,thread-safety,Java,Thread Safety,我知道从多个线程读取和写入数据需要有一个良好的锁定机制来避免数据竞争。然而,有一种情况是:如果多个线程试图用一个值写入单个变量,这可能是一个问题 例如,下面是我的示例代码: public class Main { public static void main(String[] args) { final int[] a = {1}; while(true) { new Thread(new Runnable() { @Override

我知道从多个线程读取和写入数据需要有一个良好的锁定机制来避免数据竞争。然而,有一种情况是:如果多个线程试图用一个值写入单个变量,这可能是一个问题

例如,下面是我的示例代码:

public class Main {
  public static void main(String[] args) {
    final int[] a = {1};
    while(true) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          a[0] = 1;
          assert a[0] == 1;
        }
      }).start();
    }
  }
}

我已经运行这个程序很长时间了,看起来一切都很好。如果此代码可能导致问题,我如何重现此问题?

您的测试用例没有涵盖实际问题。在同一个线程中测试变量的值-但是该线程已经复制了变量的初始状态,当变量在线程中发生更改时,该线程可以看到更改,就像在任何单线程应用程序中一样。写操作的真正问题是如何以及何时在其他线程中使用更新的值

例如,如果要编写一个计数器,其中每个线程递增数字的值,则会遇到问题。另一个问题是,测试操作比创建线程花费的时间要少,因此执行几乎是线性的。如果线程中的代码较长,则多个线程可能同时访问该变量。我使用Thread.sleep()编写了这个测试,众所周知它是不可靠的(这正是我们需要的):

int[]a=新的int[]{0};
对于(int i=0;i<100;i++){
最终int k=i;
新线程(newrunnable()){
@凌驾
公开募捐{
试一试{
睡眠(20);
}捕捉(中断异常e){
e、 printStackTrace();
}
a[0]++;
System.out.println(a[0]);
}
}).start();
}
如果执行此代码,您将看到输出是多么不可靠。数字的顺序会改变(它们不是升序),也会有重复和缺失的数字。这是因为变量被多次复制到CPU内存(每个线程一次),并在操作完成后粘贴回共享ram。(这不会在完成后立即发生,以节省时间,以防以后需要)

JVM中还可能有一些其他机制在RAM中为线程复制值,但我不知道它们


问题是,即使是锁定也不能防止这些问题。它阻止线程同时访问变量,但通常不会确保在下一个线程访问变量之前更新变量的值。

我理解这个问题。我的观点是:如果我只设置一个值(如代码示例中所示),它是否有任何副作用?如果是的话,我们如何复制这种情况?我认为数据应该被破坏。设置多少值无关紧要。如果多个线程都可以访问它,那么它们就有可能使用过期的变量值。这使得所有进一步的处理都不可靠-因此,是的,您的数据将在第一次写操作之后立即损坏。我理解您在代码示例中的观点,甚至是线程本地。但如果我们只设定一个值,这不是问题,对吗?你能详细说明一下吗?我不确定您的意思,在我的示例中,我只修改了一个值(a[0]),结果是不可靠的,即使我只修改了一次,如果其他线程可能持有该值的早期版本。如果您的意思是先修改一个值,然后将其公开给其他线程,那么从上次修改它的线程启动线程应该是安全的。
int[] a = new int[]{0};
for(int i = 0; i < 100; i++) {
    final int k = i;
    new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(20);
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
        a[0]++;
        System.out.println(a[0]);
        }
    }).start();
}