[英]unexpected multi thread output in java
public class ConTest {
@Test
void name2() {
final MyCounter myCounter = new MyCounter();
final Thread t1 = new Thread(() ->
myCounter.increment()
);
final Thread t2 = new Thread(() ->
myCounter.increment()
);
t1.start();
t2.start();
System.out.println(myCounter.count);
}
@Test
void name3() {
final MyCounter myCounter = new MyCounter();
final ExecutorService service = Executors.newFixedThreadPool(2);
for (int i = 0; i < 2; i++) {
service.execute(() -> {
myCounter.increment();
});
}
System.out.println(myCounter.count);
}
static class MyCounter {
private AtomicLong count = new AtomicLong();
public void increment() {
count.incrementAndGet();
}
}
}
AtomicLong
在多線程時是安全的。
即上面的例子中,是用2個線程執行的,所以不管執行多少次,結果都應該是2。
但是,兩次測試都嘗試了幾次后,結果有時是 1。為什么會發生這種情況?
在打印計數器的值之前,您無需等待任何后台線程或任務結束。 要等待任務退出,您需要為線程添加以下內容:
t1.join();
t2.join();
為服務添加這個,這會阻止添加新任務並等待一段合理的時間讓它們結束:
service.shutdown();
boolean done = awaitTermination(pickSuitablyLongPeriod, TimeUnit.MILLISECONDS);
確保后台任務完成后,運行時應打印正確的結果:
System.out.println(myCounter.count);
不要忘記對 Executors 使用 shutdown()
請參閱此處的評論:
// Here you start the 2 threads
for (int i = 0; i < 2; i++) {
service.execute(() -> {
myCounter.increment();
});
}
// we are not sure here that your 2 threads terminate their tasks or not !!
// the print will be executed by the Main Thread and maybe before the 2 threads terminate their
// job ,
// maybe just one terminate , maybe no one from your 2 threads increment the count .
System.out.println(myCounter.count);
您可以使用 Future 類,而不是執行您可以使用 submit() ,重運行類型將是類型 Futre<?> (accept void ) ,之后返回 Future 對象,方法 get() 將阻止執行,直到從服務返回的結果:
示例方法 name3() :將始終返回 2
void name3() {
final MyCounter myCounter = new MyCounter();
final ExecutorService service = Executors.newFixedThreadPool(2);
Future<?> f = null;
for (int i = 0; i < 2; i++) {
f =service.submit(() -> {
myCounter.increment();
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(myCounter.count);
service.shutdown();
}
這是因為當您調用System.out.println
時線程仍在處理。 在這種情況下,您需要在打印出計數器之前阻塞主線程。
在Executor
的示例中,您可以等待終止:
final ExecutorService service = Executors.newFixedThreadPool(2);
final MyCounter myCounter = new MyCounter();
for (int i = 0; i < 100; i++) {
service.submit(myCounter::increment);
}
service.shutdown();
while (!service.awaitTermination(100, TimeUnit.MILLISECONDS)) {
System.out.println("waiting");
}
System.out.println(myCounter.count);
你應該避免阻塞生產代碼,看看發布/訂閱設計模式
除了上述答案之外,您還可以添加一些打印件以更好地了解正在發生的事情。
總之。 您需要等待線程完成執行才能獲得結果,因此這不是AtomicLong
的問題。
我修改了代碼,添加了一些打印,這是執行的結果。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.Test;
public class ConTest {
@Test
void name2() {
final MyCounter myCounter = new MyCounter();
final Thread t1 = new Thread(() -> {
myCounter.increment();
System.out.println("Counter increment t1 completed and the value is " + myCounter.getCount());
});
final Thread t2 = new Thread(() -> {
myCounter.increment();
System.out.println("Counter increment t2 completed and the value is " + myCounter.getCount());
});
t1.start();
t2.start();
System.out.println(myCounter.count.get());
}
@Test
void name3() {
final MyCounter myCounter = new MyCounter();
final ExecutorService service = Executors.newFixedThreadPool(2);
for (int i = 0; i < 2; i++) {
service.execute(() -> {
myCounter.increment();
System.out.println("incrementing for count and the value is " + myCounter.getCount());
});
}
System.out.println(myCounter.count.get());
}
class MyCounter {
private AtomicLong count = new AtomicLong();
public void increment() {
count.incrementAndGet();
}
public long getCount(){
return count.get();
}
}
}
結果(名稱 2)
1
Counter increment t1 completed and the value is 1
Counter increment t2 completed and the value is 2
結果(名稱 3)
incrementing for count and the value is 1
1
incrementing for count and the value is 2
您還可以使用調試器來更好地理解。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.