Java가 코드를 매우 철저하게 최적화한다는 것을 알고 있습니다. 글쎄, 대부분의 시간.동일한 스레드에서 순서가 서로 다르게 코드가 실행됩니다.
public class BrokenOptimizationTest {
/**
* This thread constantly polls another thread object's private field.
*/
public static class ComparingThread extends Thread {
private int currentValue = 0;
private AdditionThread otherThread = null;
public ComparingThread(AdditionThread add) {
this.otherThread = add;
}
@Override
public void run() {
while (true) {
int testValue = currentValue;
if (BrokenOptimizationTest.shouldDoSomething) {
do {
testValue = otherThread.getValue();
BrokenOptimizationTest.doSomething();
// System.out.println(testValue); // to see testValue really changes
}
while (testValue == currentValue);
}
else {
do {
testValue = otherThread.getValue();
// System.out.println(testValue); // to see testValue really changes
}
while (testValue == currentValue);
}
System.out.println("{ testValue: " + testValue + ", currentValue: " + currentValue + " }");
currentValue = testValue;
}
}
}
/**
* This thread often adds to its pollable value.
*/
public static class AdditionThread extends Thread {
private int currentValue = 0;
public long queryCount = 0;
public int getValue() {
++queryCount;
return currentValue;
}
@Override
public void run() {
while (true) {
++currentValue;
//I said 'often', so sleep some more
try {
Thread.sleep(1);
}
catch (InterruptedException e) {}
}
}
}
/**
* Whether or not the program must simulate doing an expensive calculation between consecutive queries.
*/
public static boolean shouldDoSomething = false;
/**
* Simulates doing an expensive calculation
*/
public static void doSomething() {
try {
Thread.sleep(0, 100);
}
catch (InterruptedException e) {}
}
/**
* Call the program with something like "slow" to enable doSomething
*/
public static void main(String[] args) {
if (args.length >= 1 && (args[0].toLowerCase().contains("slow") || args[0].toLowerCase().contains("dosomething")))
shouldDoSomething = true;
AdditionThread addThread = new AdditionThread();
ComparingThread compThread = new ComparingThread(addThread);
addThread.start();
compThread.start();
/**
* Print the current program state every now and then.
*/
while (true) {
System.out.println("{ currentValue: " + addThread.getValue() + ", activeThreads: " + Thread.activeCount() + ", queryCount: " + addThread.queryCount + " }");
System.out.flush();
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {}
}
}
}
결과는 빠르고 느린 단일 스레드 및 멀티 스레드 프로세서에 따라 다를 수 있습니다 다음은 정말 내 머리 놨 코드의 조각이다.
{ currentValue: 1, activeThreads: 3, queryCount: 1 }
{ testValue: 1, currentValue: 0 }
{ testValue: 2, currentValue: 1 }
{ testValue: 3, currentValue: 2 }
{ testValue: 4, currentValue: 3 }
{ testValue: 5, currentValue: 4 }
{ testValue: 6, currentValue: 5 }
{ testValue: 7, currentValue: 6 }
{ testValue: 8, currentValue: 7 }
{ testValue: 9, currentValue: 8 }
{ testValue: 10, currentValue: 9 }
{ testValue: 11, currentValue: 10 }
{ testValue: 12, currentValue: 11 }
{ testValue: 13, currentValue: 12 }
{ currentValue: 994, activeThreads: 3, queryCount: 2176924819 }
{ currentValue: 1987, activeThreads: 3, queryCount: 4333727079 }
{ currentValue: 2980, activeThreads: 3, queryCount: 6530688815 }
{ currentValue: 3971, activeThreads: 3, queryCount: 8723797559 }
처음 몇 CompareThread 워크 아웃 벌금 반복하고 자바 '를 최적화': 나는 (해봐요없이) 테스트 컴퓨터에서 는, 출력은 다음과 같이 보이는 testValue 및 currentValue은 항상 동일하며 스레드가 가장 안쪽 루프를 벗어나지 않지만 계속해서 값을 변경합니다.
do {
testValue = otherThread.getValue();
currentValue = testValue; // moved up from beneath the loop
}
while (testValue == currentValue);
가 나는 그것이 성능을 향상시킬 수 있기 때문에 실행이 자바 컴파일러에서 허용되는 아웃 오브 오더 이해하지만이 : 내가 생각할 수있는 유일한 원인은, 자바, 순서가 실행을 수행과 같이이다 진술은 분명히 서로 의존적이다.
제 질문은 간단합니다 : ? 왜 Java가이 방법으로 프로그램을 실행합니까?
참고 : doSomething 매개 변수를 사용하여 프로그램을 시작한 경우 또는 AdditionThread.currentValue를 휘도가으로 설정하면 코드가 올바르게 실행됩니다.
아, 거의 설명 할 수 있습니다. 나를 괴롭히는 유일한 문제는 println의 주석 처리가 제거되었을 때 콘솔에서 testValue가 증가하는 이유는 무엇입니까? – Flumble
두 번째로 생각해 보면, testValue에 대해 위에 게시 한 내용을 재현 할 수 없어서 제 상상력이 될 수도 있습니다. 신속하고 완전한 답변 주셔서 감사합니다. :) – Flumble
println은 시스템 리소스가 사용 가능할 때까지 대기 상태에서 다시 돌아올 때 스레드가 해당 상태를 다시 설정하게하는 것과 비슷한 효과를 가질 수 있습니다. 따라서 동기화가 이루어지지 않아 발생하는 문제를 "숨길"수 있습니다. 여기를 참조하십시오 : http://stackoverflow.com/a/11570673/331052 – Affe