AtomicLong无法在线程之间正确传递

时间:2018-10-12 07:02:14

标签: java concurrency static atomic println

该练习与有效Java中的项目78有关。即,我们创建两个线程以并行方式递增公共静态变量并打印出来。目标是为控制台生成一条统一的数字递增行。 AtomicLong用于避免出现竞争状况,但是有一个我无法解释的错误。 即,首先调用

System.out.println(i.getAndIncrement());

JVM不会读取最新的变量值。仅在第二次呼叫时读取。 Please see the console output with inconsistent output marked 有人可以建议我自己学习如何解决此错误吗?现在该阅读JVM规范了吗?

package com.util.concurrency.tick;
import java.util.concurrent.atomic.AtomicLong;

public class AtomicIncrementer implements Runnable {

  private String name;
  private static final int MAXI = 1000;
  private static final AtomicLong i = new AtomicLong(-1);

  public AtomicIncrementer(String name){
    this.name = name;
  }

  public void run(){
    while(i.get() < MAXI){
      System.out.println(name+ ". i = "
        +i.getAndIncrement());
    }
    System.out.println(name+" i = "+i.get()); 
  }

  public static void main(String[] args){;
         try {
              Thread t1 = new Thread(new AtomicIncrementer("A"));
              Thread t2 = new Thread(new AtomicIncrementer("B"));
              t1.start();
              t2.start();
            } catch (Exception e) {
            }
        System.out.println("Two incrementers launched");
   }
}

4 个答案:

答案 0 :(得分:0)

基本上您没有使用同步

您可以如下更改run方法:

public void run(){

      synchronized(i){
    while(i.get() < MAXI){
      System.out.println(name+ ". i = "
        +i.getAndIncrement());
    }

    System.out.println(name+" i = "+i.get());
      }
  }

此外,您可以参考本教程以获取有关同步的更多信息:https://www.tutorialspoint.com/java/java_thread_synchronization.htm

答案 1 :(得分:0)

这可能是您要尝试执行的操作:)请检查Java文档。您要在2个线程之间进行同步。基本上,每个线程递增一圈,然后将计数器传递给另一个线程。

public class AtomicIncrementer implements Runnable {

    private static final int MAXI = 1000;
    private static final SynchronousQueue<Long> LONG_EXCHANGER = new SynchronousQueue<>();

    private final String name;

    private AtomicIncrementer(String name) {
        this.name = name;
    }

    @Override
    public void run() {

        try {

            while (true) {

                Long counter = LONG_EXCHANGER.take();
                if (counter >= MAXI) {
                    LONG_EXCHANGER.put(counter);
                    break;
                }

                System.out.println(name + ". i = " + (counter + 1));
                LONG_EXCHANGER.put(counter + 1);
            }

            Long counter = LONG_EXCHANGER.take();
            System.out.println(name + " final i = " + counter);
            LONG_EXCHANGER.put(counter);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        Thread t1 = new Thread(new AtomicIncrementer("A"));
        Thread t2 = new Thread(new AtomicIncrementer("B"));
        t1.start();
        t2.start();
        System.out.println("Two incrementers launched");

        try {
            LONG_EXCHANGER.put(-1L);
            t1.join();
            System.out.println("T1 ended");
            //this is needed for last thread to end
            LONG_EXCHANGER.take();
            t2.join();
            System.out.println("T2 ended");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

答案 2 :(得分:0)

同步不是问题,因为AtomicLong仅用于此目的。 相反,必须在线程切换时将共享字段从&更新到本地线程内存。为此存在volatile

private static final volatile AtomicLong i = new AtomicLong(-1);

实际上我并不完全确定!对于long,情况很明显, 但是只有一个物体;其长字段正在更新。

答案 3 :(得分:0)

您在这里有很多比赛条件。 以下事件顺序将导致您看到结果:

线程A一直运行到i=198。然后有上下文切换,线程B运行以下命令:

System.out.println(name+ ". i = "
        +i.getAndIncrement());
在while循环内

。 线程B创建字符串:

“ B。我= 198”

但是在线程B有机会打印该字符串之前,再次有上下文切换到线程A。 因此线程A继续执行,直到打印

  

A。 i = 204

然后有一个上下文切换到线程B,它从之前停止的位置恢复,该位置正在打印字符串:

  

B。我= 198

基本上,在获得i的当前值并打印之前,您已经处于竞争状态。

换句话说,i.getAndIncrement();是原子操作。 但是

System.out.println(name+ ". i = "
        +i.getAndIncrement());

不是原子操作。

您在这里有多项操作。用伪代码:

 1. tempInt = i.getAndIncrement();
 2. tempString1 = name + ". i = ";
 3. tempString2 = convertToString(tempInt);
 4. tempString3 = tempString1 + tempString2;
 5. print tempString3;

这就是为什么您会有如此混乱的输出:)

如果您想深入了解这些概念,我建议您参加此在线课程: https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY

它真的很便宜,您可以在几个小时内完成它。但是,对于那些概念和原子类的警告确实有更深入的介绍。这是一笔很棒的时间投资。

在您的情况下,您要做的实际上是使用同步而不是AtomicLong,因为这里的内容是多操作关键部分。

希望对您有帮助。