ArrayListにオブジェクトを追加する二つのスレッド:できない出力サイズ:なぜ無限ループにprintlnのリードを取り除くん

Cyber​​Wolf:

私はこのプログラムを持っています:

Farmer.java

import java.util.*;

public class Farmer implements Runnable  {
  List<Apple> busket;
  private static int count;
  private int id;
  private int appleCount;

  public Farmer(List<Apple> busket)  {
    this.busket = busket;
    id = count++;
  }

  public void makeApple()  {
    Apple apple = new Apple("Apple" + appleCount + " by Farmer"+id);
    System.out.println("making apple: " + apple);

    busket.add(apple);
    appleCount++;
  }

  public void run()  {
    while (appleCount<5)  {
      makeApple();
    }
  }
}

Apple.java

public class Apple  {
  private String name;

  public String getName() {return name;}

  public String toString() {return name;}

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

Main.java

import java.util.*;

class Main {
  public static void main(String[] args) throws Exception {
    System.out.println("Hello world!");

    List<Apple> apples = new ArrayList<>();
    Thread t1 = new Thread(new Farmer(apples));
    t1.start();

    Thread t2 = new Thread(new Farmer(apples));
    t2.start();

    while (apples.size()==0) {
  //    System.out.println(apples.size());
    }

    System.out.println(apples.size());
}
}

それは、この出力を与えます:

Hello world!
making apple: Apple0 by Farmer1
making apple: Apple1 by Farmer1
making apple: Apple2 by Farmer1
making apple: Apple3 by Farmer1
making apple: Apple4 by Farmer1
making apple: Apple0 by Farmer0
making apple: Apple1 by Farmer0
making apple: Apple2 by Farmer0
making apple: Apple3 by Farmer0
making apple: Apple4 by Farmer0

プリント決してそれはそう

System.out.println(apples.size());

しかし、もし私のコメントを解除

//    System.out.println(apples.size());

内側のwhileループ

私はこの出力を得ます:

...many more zeroes
0
0
0
making apple: Apple0 by Farmer1
0
0
...a bit more zeroes
1
making apple: Apple0 by Farmer0
making apple: Apple1 by Farmer0
making apple: Apple2 by Farmer0
making apple: Apple3 by Farmer0
making apple: Apple4 by Farmer0
making apple: Apple1 by Farmer1
making apple: Apple2 by Farmer1
making apple: Apple3 by Farmer1
making apple: Apple4 by Farmer1

あなたが見ることができます

1

アレイのサイズは上記出力、です。

プログラムは停止しているようだことはありません。

私はまた、このループを試してみました:

    int i=0;
    while (apples.size()==0) {
      System.out.println("i: " + i); //works as previous: if uncommented, there's size output later, if commented out, no size output, program keeps running
    }

そして

    int i=0;
    while (apples.size()==0) {
      //System.out.println("i: " + i);
      i++; //doesn't influence - no size output unless uncomment system.out.println
    }

そして、それは同じ動作をします

なぜこの動作はありますか?whileループ内のprint文を追加するなぜプログラムを得るように思わ除去しながら、正しい出力(配列のサイズとプログラムの仕上げ)を与える配列がいっぱいになってもかかわらず、whileループで立ち往生?

EDIT

私はそれが同期リストを使用して解決することができます知っています。私は、これは、それは、コードを修正しないように動作します理由を理解したいと思います。

モニカの上に左hmakholm:

クイックTL; DRソリューション:いくつか挿入しsynchronized、すでに共有リストへのアクセス保護への声明!


Javaは、複数のスレッドが同じ変数にアクセスするときに物事が正常に動作することを保証するものではありません明示的に同期されず

あなたが持っている場合には

while (apples.size()==0) {
    //    System.out.println(apples.size());
}

JITがに許可されています

  1. ボディインラインapples.size()最も可能性が高いだけで、リストオブジェクト内のプライベート変数を読み取るして、

  2. その後、コード発する一度だけの変数を読み込み、ヒープから、それは一度、すべてのレジスタに格納されていることサイズのコピーをテストしておくの。他のスレッドが行う何もサイズ情報のこのローカルにキャッシュされたコピーを変更しません。だから、リストの長さがあるために開始テストは永遠に表示されるものです0で、 -あなたは無限ループを持っています。

Javaの同時実行モデルは、スレッドが、別のスレッドを作ることをヒープフィールドへの変更を見ることができるように保証されていないことを言うのでJITがあることを行うことが許されるまで、両方のスレッドがいくつかの適切な同期アクションを通過しました。

あなたが呼び出しを挿入するときにSystem.out.println()何が起こるかは、ループのボディは今JITがそれを証明することができないほど複雑であることであるSystem.out.println()ではないだろう何とかリストオブジェクト内の状況を変えます。私たちは、それはしませんが、JITは非常に少ない脳のクマで、できないことを簡単に確認することができます)。したがってそれはループの前の反復において読み出さサイズフィールドの値を共有してあえてしません。代わりに、それは一回のループの周りのたびに、ヒープオブジェクトからサイズフィールドを読み込みますマシンコードを作成します。したがって、 -間違いなくより多くの設計によって、より偶然!-あなたのメインスレッドはなり最終的には他のスレッドからの変更を拾います。


それでも変更しようとしている2つの異なるスレッド持っているので、これは平均値すべてが、罰金、今ではありませんArrayListお互いのつま先を踏んでからそれらを防ぐために、何も。奇妙なことのすべての方法は、間違って行くことがあります。それはちょうどかもしれあなたが意図した通りの仕事に起こります。あなたは本当にラッキー場合は、あなたは素敵な明示的に得ることができますConcurrentModificationExceptionいずれかのスレッドでスロー。またはりんごの一つ以上が静かに失われる可能性がある、またはあなたが発生する可能性がありNullPointerExceptionます。後でりんごを取るしようとした場合、またはより悪いから再びリストの。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=318071&siteId=1