繁体   English   中英

java中的意外多线程输出

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM