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
}
}
假设代码执行分为两个线程。假设发生以下一系列事件:
- 第一个线程访问
方法并缓存当前为0的getNum
李>internalNum
- 同时,第二个线程访问获取锁的
方法,将modifyNum
更改为1并退出释放锁李>internalNum
- 现在,第一个线程继续执行并打印
李>internalNum
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);