Java同步和数据一致性

Java同步和数据一致性,java,memory,synchronized,volatile,flush,Java,Memory,Synchronized,Volatile,Flush,考虑以下简单示例: public class Example extends Thread { private int internalNum; public void getNum() { if (internalNum > 1) System.out.println(internalNum); else System.out.println(1000); } publi

考虑以下简单示例:

public class Example extends Thread {
    private int internalNum;

    public void getNum() {
        if (internalNum > 1)
            System.out.println(internalNum);
        else 
            System.out.println(1000);
    }

    public synchronized modifyNum() {
        internalNum += 1;
    }

    public void run() {
        // Some code
    }
}
假设代码执行分为两个线程。假设发生以下一系列事件:

  • 第一个线程访问
    getNum
    方法并缓存当前为0的
    internalNum
  • 同时,第二个线程访问获取锁的
    modifyNum
    方法,将
    internalNum
    更改为1并退出释放锁
  • 现在,第一个线程继续执行并打印
    internalNum
问题是控制台上会打印什么

我的猜测是,这个假设的示例将导致在控制台上打印1000,因为只有在进入或离开同步块时,读写刷新才会强制在特定线程上进行。所以,第一个线程将愉快地使用它的缓存值,而不知道它被更改了

我知道使
internalNum
易失性可以解决可能的问题,但我只是想知道这是否真的有必要

假设代码执行分为两个线程。 它不会退出。但是,两个线程可以并发访问ressource(方法、字段)

我觉得你把事情搞混了。您的类扩展了
线程
,但您的问题是通过并发线程访问同一实例的资源

下面是适合您的问题的代码

线程之间的共享资源:

public class SharedResource{
    private int internalNum;

    public void getNum() {
        if (internalNum > 1)
            System.out.println(internalNum);
        else 
            System.out.println(1000);
    }

    public synchronized modifyNum() {
        internalNum += 1;
    }

    public void run() {
        // Some code
    }   
}
线程和运行代码:

public class ThreadForExample extends Thread {

    private SharedResource resource;

    public ThreadForExample(SharedResource resource){
       this.resource=resource;
    }

    public static void main(String[] args){
       SharedResource resource = new SharedResource();
       ThreadForExample t1 = new ThreadForExample(resource);
       ThreadForExample t2 = new ThreadForExample(resource);
       t1.start();
       t2.start();
    }
}
你的问题:

假设发生以下一系列事件:

第一个线程访问getNum方法并缓存internalNum 现在是0。同时,第二个线程访问 modifyNum方法获取锁,将internalNum更改为1,然后 释放锁。现在,第一个线程继续执行它并 打印internalNum

在您的场景中,您给人的印象是
modifyNum()
方法执行会阻止其他线程访问非同步方法,但事实并非如此。
getNum()
未同步。因此,线程不需要获取对象上的锁来执行它。在这种情况下,输出仅取决于哪个线程第一次执行了指令:

internalNum += 1;


Java内存模型中没有缓存和刷新的概念。正如您所说,getNum()不能保证看到与0不同的东西。在您的情况下,我将删除synchronized并使用AtomicInteger。internalNum不是静态的,不需要同步对它的访问,因为它本身是一个线程对象,每个线程都有自己的internalNum副本。@AlexC您几乎是对的。你不知道我是如何运行线程的。我可以写:
Example myExample=newexample();线程t1=新线程(myExample);线程t2=新线程(myExample);t1.start();t2.start()当然,我不会这么做。是的,基于您将基类设置为线程而不是可运行的事实,不清楚您是否打算将其作为可运行的调用,同时保留从线程继承时添加的所有额外权重。现在,由于代码中对internalNum的访问未同步,因此行为未定义。您需要同步getter或使用AtomicInteger。我认为如果您只看到0,那么它将符合规范,但是我想不出任何真实世界的vm/体系结构会以这种方式运行。有一些testvm标志的行为确实如此。例如,这里这不是我的意思,我完全知道modifyNum不会阻止其他执行块,getNum也不会被阻止。这只是可能发生的一系列事件的假设。而且,我不太确定你的回答是否正确。参考我帖子上的评论。控制台上打印的内容不仅仅取决于执行顺序。有可能
internalNum+=1首先执行,但由于第一个线程的线程本地缓存,1000会被打印出来。好的,我理解您的询问。你的意思是getNum()被线程调用,然后线程就停止了?是的,在这种情况下,我想说的是,结果实际上是不可预测的,因为它取决于多个因素:CPU、硬件优化、操作系统和虚拟机版本。但是无论如何,没有它,你已经有了一种不可预测的行为:)是的,类似这样的。第一个线程不必停止,而是以某种方式暂停,而第二个线程则不间断地执行。
 System.out.println(internalNum);