![](/img/trans.png)
[英]Java multithreading - joining a CPU heavy thread and volatile keyword
[英]Java volatile keyword in multithreading
我试图在Java多线程中重现非易失性变量的行为。 在这里,我在OccurrenceCounter.java类中进行了非易失性变量test
。 在ThreadDemo.java类中,我具有生成两个线程的main
方法。 在thread1
它在while循环中连续检查非易失性变量test
的值。 如果我在MyRunnableThread1.java中的以下示例中运行,则不会从CPU高速缓存中获取encesCounter.isTest occurrenceCounter.isTest()
的值。
OccurrenceCounter.java:
public class OccurrenceCounter {
// non-volatile variable
private boolean test=true;
public OccurrenceCounter(boolean test)
{
this.test=test;
}
public boolean isTest() {
return test;
}
public void setTest(boolean test) {
this.test = test;
}
}
MyRunnableThread1.java:
// Thread 1
public class MyRunnableThread1 implements Runnable {
private String threadName;
private OccurrenceCounter occurrenceCounter;
public MyRunnableThread1(String threadName,OccurrenceCounter occurrenceCounter)
{
this.threadName=threadName;
this.occurrenceCounter=occurrenceCounter;
}
@Override
public void run() {
System.out.println("Thread "+threadName + " started");
System.out.println("value of flag:"+occurrenceCounter.isTest());
int i=0;
// random processing code
while(i<1000000000L)
{
i++;
i++;
i++;
}
// checking whether non-volatile variable is taken from cpu cache
while(occurrenceCounter.isTest())
{
}
System.out.println("Thread "+threadName + " finished");
}
}
MyRunnableThread2.java:
// Thread 2
public class MyRunnableThread2 implements Runnable {
private String threadName;
private OccurrenceCounter occurrenceCounter;
public MyRunnableThread2(String threadName,OccurrenceCounter occurrenceCounter)
{
this.threadName=threadName;
this.occurrenceCounter=occurrenceCounter;
}
@Override
public void run() {
System.out.println("Thread "+threadName + " started");
occurrenceCounter.setTest(false);
System.out.println("Thread "+threadName + " finished");
}
}
ThreadDemo.java:
public class ThreadDemo {
public static void main(final String[] arguments) throws InterruptedException {
System.out.println("main thread started");
OccurrenceCounter occurrenceCounter =new OccurrenceCounter(true);
MyRunnableThread1 myRunnableThread1=new MyRunnableThread1("Thread1", occurrenceCounter);
MyRunnableThread2 myRunnableThread2=new MyRunnableThread2("Thread2", occurrenceCounter);
Thread t1=new Thread(myRunnableThread1);
Thread t2=new Thread(myRunnableThread2);
t1.start();
try
{
Thread.sleep(100);
}
catch(Exception e)
{
System.out.println("main thread sleep exception:"+e);
}
t2.start();
System.out.println("main thread finished");
}
}
在这里,在MyRunnableThread1.java中的while循环中,即使OcuurenceCounter
类中的test
变量是非易失性的,也不会从CPU缓存中返回条件enceingCounter.isTest occurrenceCounter.isTest()
。 但是,如果我删除第一个while循环:
while(i<1000000000L)
{
i++;
i++;
i++;
}
然后,我可以看到,即使条件thread2
更新了thread2
并且thread1
从未终止,条件thread1
occurrenceCounter.isTest()
始终为false。 那么为什么这个while循环:
while(i<1000000000L)
{
i++;
i++;
i++;
}
影响非易失性变量的行为? 这是第一次同时强制从内存而不是从thread1
CPU缓存中读取值的while循环吗? 我试图得到很多答案。 但是我不能。
请任何人帮我解决这个问题。
使用volatile
,JMM可以确保更新的值可用于其他线程。
如果没有volatile
或其他同步,则更新的值可能会或无法用于其他线程,这是不确定的。
在这种情况下,循环
while(i<1000000000L) {
i++;
i++;
i++;
}
无法保证变量test
内存可见性,这只是巧合。 不要依赖它。
volatile
关键字可确保如果一个Thread
对该变量进行了某些更改,那么在更改后读取该变量的其他Thread
将看到该变量。 如果使用普通的非易失volatile
变量,则其他Threads
可能会也可能不会看到它。 它取决于JVM的内部。
那么,如何解决呢? 有几种方法:
如前所述,您可以使其变得volatile
。 这可能是最简单的方法:
public class OccurrenceCounter { private volatile boolean test=true; // ... }
您可以使访问器synchornized
。 在这种情况下,您必须同步所有变量访问:
public class OccurrenceCounter { private boolean test=true; public OccurrenceCounter(boolean test) { this.test=test; } public synchronized boolean isTest() { return test; } public synchronized void setTest(boolean test) { this.test = test; } }
您可以使用AtomicBoolean
来为您处理所有这些。 您为此付出的代价是该API会更加冗长。 除非您想使用compareAndSet()
或getAndSet()
方法,否则这可能是一个getAndSet()
:
private AtomicBoolean test = new AtomicBoolean(true); public OccurrenceCounter(boolean test) { this.test.set(test); } public boolean isTest() { return test.get(); } public void setTest(boolean test) { this.test.set(test); }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.