2016-07-29 5 views
4

この質問は2,3ヶ月前にSkypeを介してドイツ企業に聞き取りました。次のコードが与えられた場合:インタビューQ:Java同期

private static DateFormat DATE_FORMAT = new SimpleDateFormat();  
public void doSomething() { 
    for (int i = 0; i < 100; i++) { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       synchronized (DATE_FORMAT) { 
        System.out.println(DATE_FORMAT.format(Calendar.getInstance().getTime())); 
       } 

      } 
     }).start(); 
    } 
} 

潜在的な同期の問題があり、その理由がある場合は、状態。

私の直感は、何もないはずがないと私に伝えます。 100個のスレッドを作成していますが、それぞれが同じオブジェクト(DATE_FORMAT)のロックを取得し、現在の時刻を多かれ少なかれ正確に表示します。しかし、私は面接官が印刷の不一致について何か言及したが、正しく思い出すことはできないことを覚えています。

ありがとうございます。

+2

'' System.out.println''は、スレッドセーフであることが保証されていませんが、私はあなたのためにすべての問題が表示されません並行して呼び出すのではなく、同期アクセスを使用します。 –

+0

実際ではない値が出力される可能性がありますか?たとえば、別のスレッドが印刷中に値を変更する可能性がありますか? (たとえそれがこのシナリオではないとしても) – Santi

+0

おそらくインタビュアーがあなたの足を引っ張っていたかもしれません...私が見ている唯一の問題(私はこのケースでは問題として扱わない)は、スレッドが創造の秩序。タイムを取得し、その場でプリントするので、害はありません。 –

答えて

1

単一のモニタ(DATE_FORMAT)によって制御されるクリティカルセクションがあり、他のロックが存在しないため、デッドロックの危険性がないため、問題はありません。

DATE_FORMATフィールドは最終的なものではないため、他のコードでも参照先が変更される可能性がありますが、これを主に使用すると問題は発生しません。format同じのインスタンスSimpleDateFormatが同時に発生します。

+1

「SimpleDateFormat」のドキュメントでは、次のように書かれています。 日付の形式は同期されていません。 スレッドごとに別々の書式インスタンスを作成することをお勧めします。 複数のスレッドが同時にフォーマットにアクセスする場合は、外部で を同期させる必要があります。これはここで行われている処理とまったく同じです。 –

+0

しかし、 'format()'は同時にアクセスされません。 'synchronized'ブロックはそれを防ぎます。 –

+0

はい、それは私がそれがいいと言った理由です。申し訳ありませんが私の答えが多分少し不明だった場合。 –

1

与えられたコードに従って;

DATE_FORMATのインスタンスからの呼び出し形式メソッドは、DATE_FORMATのインスタンスにあるカレンダーオブジェクトを変更しているため、他のスレッドが印刷する前にカレンダーを修正する可能性があります(他のスレッドはカレンダーオブジェクトを変更したスレッドですが、まだ印刷されていません)。ここ

がSimpleDateFormat.class

// Called from Format after creating a FieldDelegate 
private StringBuffer format(Date date, StringBuffer toAppendTo, 
          FieldDelegate delegate) { 

// Convert input date to time field list 
calendar.setTime(date); // modifies the calender's instance 

オーケーで参照され、しかしDATE_FORMATのロックはありませんDATE_FORMAT内のカレンダーを変更するには、他のスレッド を防ぐ必要がありますか? - @Santi

直接カレンダーを修正するために防止されていないが、2スレッドは1つのスレッドが待機しなければならない(DATE_FORMATのインスタンスである)同じ引数と同時に、同期ブロックを実行しようとすると、それは、DATE_FORMATのインスタンスにアクセス防止されます同期ブロックを実行する別のスレッド同期の仕組み

私はコメントで約束したように、私は私の答えを証明するためにシミュレーションを行いました。

private static DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");  
    private static ArrayList<String> listDate = new ArrayList<>(); 

    public static void doSomething() 
    { 
     for (int i = 0; i < 100; i++) { 

      final long msCurrentDate = i*100; 

      new Thread(new Runnable() { 

       public void run() { 
        synchronized (DATE_FORMAT) { 
         listDate.add(DATE_FORMAT.format(new Date(msCurrentDate))); 
         //System.out.println(DATE_FORMAT.format(Calendar.getInstance().getTime())); 
        } 
       } 
      }).start(); 
     } 


     Runtime.getRuntime().addShutdownHook(new Thread() 
     { 
      @Override 
      public void run() 
      { 
       int resultSize = listDate.size(); 
       System.out.println("All elements' size :" + resultSize); 
       resultSize = listDate.stream().distinct().collect(Collectors.toList()).size(); 
       System.out.println("Unique elements' size :" + resultSize); 
      } 
     }); 


    } 

目的を変更せずにコードを修正しました。ご覧のとおり、同期されたバージョンと同期されていないバージョンのコードとの結果を比較するために、固定(スレッドごとに100msを増やす)時間を使用しています。

私はDatesを印刷しており、Look&Feelを比較するのではなく、数字を扱うStringのArrayListにDatesを追加しています。

まず私が印刷された結果を追加してみましょう:

enter image description here

左側に印刷された2複数の日があり、右側には何の複数日のコース最初の5つの結果の

はありません何も証明していない、あなたはそれらのすべてをチェックしなければならない。 だから私はここで、リストから

を同じエントリを削除した後、一覧と印刷結果に結果を追加した同期済みバージョンの結果である:ここで

//Output of all executions 
//All elements' size :100 
//Unique elements' size :100 

は同期されていないバージョンの結果である:

//Output of execution : 1 
//All elements' size :100 
//Unique elements' size :82 

//Output of execution : 2 
//All elements' size :100 
//Unique elements' size :78 

//Output of execution : 3 
//All elements' size :100 
//Unique elements' size :81 

結果によると、カレンダーはA、B、C ...の前にXスレッドによって変化していると言えるでしょう。リスト)

ストリームAPIを使用するにはJDK 8が必要です。また、他のコードを使用することもできます。私たちが議論できるようにご質問がありましたらお知らせください。

+0

しかし、DATE_FORMATのロックは、他のスレッドがDATE_FORMATの中のカレンダーを変更できないようにする必要がありますか? – Santi

+0

私の答えを編集しましたあなたがコメントで入力したシナリオのサンプルプロジェクトをすでに持っています。アップロードすることもできますし、自分で試すこともできますが、日付の問題の例を作り、結果を共有しようとします –

1

フォーマッタとしてnew SimpleDateFormat("HH:mm:ss.SSS")を使用してください。ほんの少しの時間を必要とする奇妙な時間を得るには、分の境界に合わせるタイミングが必要です。

私は自信を持って、現状のコードと同期の問題はないと言います。さまざまな呼び出しのスレッド安全性に関する懸念は、同期ブロックによって休止され、すべての初期化が順番になっているように見えます。特定の悪夢は実際に私に飛び出すことはありません。

Calendar.getInstance().getTime()コールが同期ブロック外で作成/割り当てされ、その内で使用された場合には、意味をなさないことがあります。同期ブロックは、到着した順序でロックを待っているスレッドを必ずしも呼び出すわけではなく、出力の順序が間違っている可能性がありますが、現在のコードではそうではありません。私が示唆できるのは、インタビュアーが間違ったコードを提示したか、間違っていた可能性があります。

参考のために、次のコードが生成されますアウト・オブ・オーダーインターリーブ:

public class Test { 
    private static DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS"); 
    public static void main(String[] args) { 
     for (int i = 0; i < 100; i++) { 
      new Thread(new Runnable() { 
       @Override 
       public void run() { 
        Date time = Calendar.getInstance().getTime(); 
        synchronized (DATE_FORMAT) { 
         System.out.println(DATE_FORMAT.format(time)); 
        } 
       } 
      }).start(); 
     } 
    } 
} 
関連する問題