简体   繁体   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 is safe when multi thread. AtomicLong在多线程时是安全的。

That is, in the example above, it was executed with 2 threads, so the result should be 2 no matter how many times it is executed.即上面的例子中,是用2个线程执行的,所以不管执行多少次,结果都应该是2。

However, after trying both tests several times, the result is sometimes 1. Why is this happening?但是,两次测试都尝试了几次后,结果有时是 1。为什么会发生这种情况?

You aren't waiting for any of the background threads or tasks to end before you print the value of the counter.在打印计数器的值之前,您无需等待任何后台线程或任务结束。 To wait on the tasks to exit, you'll need to add this for threads:要等待任务退出,您需要为线程添加以下内容:

t1.join();
t2.join();

Add this for the service, which prevents new tasks being added and waits a sensible period for them to end:为服务添加这个,这会阻止添加新任务并等待一段合理的时间让它们结束:

service.shutdown();
boolean done = awaitTermination(pickSuitablyLongPeriod, TimeUnit.MILLISECONDS);

Once you have ensured the background tasks are completed, the correct result should be printed when you run:确保后台任务完成后,运行时应打印正确的结果:

System.out.println(myCounter.count);

dont forget to use shutdown() with Executors不要忘记对 Executors 使用 shutdown()

see the comments here :请参阅此处的评论:

   // 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);

You can use Future class , instead of execute you can use submit() , the retrun type will be of Type Futre<?> (accept void ) , after that with the Future object returned the method get() will block the execution until the result returned from the service :您可以使用 Future 类,而不是执行您可以使用 submit() ,重运行类型将是类型 Futre<?> (accept void ) ,之后返回 Future 对象,方法 get() 将阻止执行,直到从服务返回的结果:

Example method name3() : will return always 2示例方法 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();
    }

This is because the threads are still processing when you call the System.out.println .这是因为当您调用System.out.println时线程仍在处理。 In this case you would need to block the main thread before you print out the counter.在这种情况下,您需要在打印出计数器之前阻塞主线程。

in the example of the Executor you can just await the termination: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);

you should avoid to block in productive code, have a look at the Publish/Subscribe design pattern你应该避免阻塞生产代码,看看发布/订阅设计模式

In addition to the above answers, you could add some prints to better understand what is happening.除了上述答案之外,您还可以添加一些打印件以更好地了解正在发生的事情。

In summary.总之。 You need to wait for the threads to finish executing before expecting the results, so it is not an issue of AtomicLong .您需要等待线程完成执行才能获得结果,因此这不是AtomicLong的问题。

I modified the code, added some prints, and here are results from an execution.我修改了代码,添加了一些打印,这是执行的结果。

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();
    }
  }
}

Results (name2)结果(名称 2)

1
Counter increment t1 completed and the value is 1
Counter increment t2 completed and the value is 2

Results (name3)结果(名称 3)

incrementing for count and the value is 1
1
incrementing for count and the value is 2

You could also use a debugger to have a better understanding.您还可以使用调试器来更好地理解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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