[英]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.