2009-06-11 4 views
10

私のJavaプロジェクトでは、私が書いたほとんどすべての非静的メソッドは​​です。私は今日、いくつかのコードを修復することを決めました。​​キーワードのほとんどを削除しました。そこには、パフォーマンスを向上させることなく、修正するのにかなりの時間を要したいくつかのスレッド問題がありました。結局私はすべてを取り返した。「同期」キーワードをどこにでも叩かない理由は何ですか?

"​​"というコードを書いている人はいません。だから何か理由がありますかにはどこにでも "​​"がありますか?

パフォーマンスがあまり気にしない(つまり、メソッドが数秒に1回以上呼び出されない)場合はどうすればよいですか?

+13

ブラックボックスの材料から飛行機を作っていないのと同じ理由は... –

答えて

26

もちろんパフォーマンスです。モニターにはコストがあります。

答えは、ランダムな方法で同期を取り除くことも、同期を追加することもありません。 Brian Goetzの "Java Concurrency In Practice"やDoug Leaの "Java Threads"などの本を読んで、正しく実行する方法を理解することをお勧めします。もちろん、新しいコンカレントパッケージをよく学びます。

マルチスレッド化はsynchronizedキーワードよりもはるかに多いです。

+4

同期のコストよりもはるかに大きな心配があります。 – Eddie

+11

synchronizedキーワードをメソッドに追加する人など、実際になぜ、またはそれが何であるかを理解することなく、大きな心配があります。私はそれについて心配するだろう。 – izb

+4

プラス:同期の不適切な過度の投与がデッドロックを引き起こす可能性があります – Dirk

2

ちょうどそれは物事を遅くすることをほぼ確実にします。

すべてのsync'edオブジェクトは、まだ使用されていないことを確認するために何らかのチェックを実行し、使用時にフラグを設定し、終了時にリセットしなければなりません。

これには時間がかかります。機能的な変更は気づかないかもしれませんが、パフォーマンスが重要な場合は気を付ける必要があります。

7

パフォーマンスポイントはすでに述べられています。

また、すべてのスレッドがスタックの異なるコピーを取得することに注意してください。したがって、メソッドがそのメソッド内で作成された変数のみを使用し、外部の世界(ファイルハンドル、ソケット、DB接続など)にアクセスできない場合、スレッドの問題が発生する可能性はありません。

+0

私はそれを正確に言うつもりでした。 –

36

無差別に同期させる場合は、deadlockを作成するリスクもあります。

FooBarの2つのクラスがあり、いずれも同期メソッドdoSomething()を持っているとします。さらに、各クラスが、他のクラスのインスタンスをパラメータとして持つ同期メソッドを持つとします。

public class Foo { 

    synchronized void doSomething() { 
     //code to doSomething 
    } 

    synchronized void doSomethingWithBar(Bar b) { 
     b.doSomething(); 
    } 

} 

public class Bar { 

    synchronized void doSomething() { 
     //code to doSomething 
    } 

    synchronized void doSomethingWithFoo(Foo f) { 
     f.doSomething(); 
    } 
} 

あなたはFooのインスタンスとBarのインスタンスを持って、両方同時にお互いに自分のdoSomethingWith*メソッドを実行しようとした場合、デッドロックが発生する可能性があることを見ることができます。

デッドロックを強制するには、あなたがdoSomethingWith*方法(例としてFooを使用)の両方で睡眠を挿入できます。

:あなたの主な方法で

synchronized void doSomethingWithBar(Bar b) { 
    try { 
     Thread.sleep(10000); 
    } catch (InterruptedException ie) { 
     ie.printStackTrace(); 
    } 

    b.doSomething(); 
} 

、次の2つのスレッドの例を完了するために開始します

+4

デッドロックがなくても、大きな問題に陥る可能性があります。私はちょうど1週間に数千回呼ばれ、約20ミリ秒かかる同期ブロック内のI/O操作によって打撃を受けました。問題ありません。しかし、いくつかの奇妙な理由から、週に2〜3回は30分かかります。おお! –

12

"同期"キーワードをどこにでも置くことは、パフォーマンスを無視しても良い解決策だと思うなら、何が起こっているのかを正確に理解していないので、使用しないでください。私はその話題に目を向ける前に、読んでみることを強く勧めます。

スレッディングは習得するのが非常に難しいトピックであり、なぜあなたが何をしているのかを理解することは本当に重要です。さもなければ、設計よりもむしろうまく動作する多くのコードを作成するでしょう。これはあなたの痛みを引き起こすだけに終わるでしょう。

+6

以上、まだスレッドを使用しないでください。 –

6

​​をどこにでも置くよりも、あなたのクラスが必要とする不変量を注意深く推論してから、それらの不変量を保証するのに十分なだけ同期してください。

  1. デッドロック
  2. 生存性の問題

デッドロックの誰もが知っている:あなたの過同期する場合、次の2つのリスクを作成します。ライブラリーの問題は、同期のコストとは関係ありませんが、マルチスレッド・アプリケーションですべてをグローバルに同期させると、モニターを待つことを待っているスレッドがブロックされます。なぜなら、別のスレッドは、モニター。

安全のためにキーワードをどこにでも叩きたい場合は、​​の代わりにfinalを使用することをおすすめします。 :)finalを作ることで、スレッドの安全性が向上し、ロックによってどの不変条件を維持する必要があるのか​​を簡単に推論できます。一例を与えるために、あなたはこの些細なクラスを持っているとしましょう:上記のクラスで

public class SimpleClass { 
    private volatile int min, max; 
    private volatile Date startTime; 
    // Assume there are many other class members 

    public SimpleClass(final int min, final int max) { 
     this.min = min; 
     this.max = max; 
    } 
    public void setMin(final int min) { 
     // set min and verify that min <= max 
    } 
    public void setMax(final int max) { 
     // set max and verify that min <= max 
    } 
    public Date getStartTime() { 
     return startTime; 
    } 
} 

を、minまたはmaxを設定するとき、あなたは同期させる必要があります。上記のコードは壊れています。このクラスはどのような不変式を持っていますか?複数のスレッドがsetMinまたはsetMaxを呼び出す場合でも、常にmin <= maxを確保する必要があります。

1つのスレッドがsetMinを呼び出し、別のスレッドがgetStartTimeを呼び出して、第二のスレッドが不必要にsetMin戻るまでブロックされます、その後場合は、これは多くの変数の大きなクラスであり、あなたはすべてを同期すると仮定。多くのメンバーを持つクラスでこれを行い、守らなければならない不変条件に少数のメンバーしか関与していないと仮定すると、すべてを同期させることはこの種の多くの生存問題を引き起こします。

1

は、あなたが、あなたがそれらを中断することはできませんいくつかのスレッドがロックされている場合は、この例

 

synchronized (lock){ 
    doSomething(); 
} 
 

があるとしましょう。ロックが発生する可能性のある割り込みをより適切に制御できるため、ロックオブジェクトを "synchronized"の代わりに使用することをお勧めします。 もちろん、すべての提案と同様に、「同期」に関するパフォーマンス上の問題もあります。それらを過度に使用すると、デッドロック、ライブロックなどが発生する可能性があります。

2

あなたはデザインを再考する必要があります。私の経験から言うと、このレベルの同期はスケーラビリティではありません。すぐに問題が解決するかもしれませんが、アプリケーション全体の完全性を修復するのには役立ちません。

例:「同期」コレクションを持っていても、一方のスレッドは2つの「同期」メソッドを呼び出すことができますが、別のスレッドに中間結果を表示させたくありません。したがって、きめ細かな "同期" APIの呼び出し元は、それ自体を認識する必要があります。これは、このAPIの(プログラマーの)使いやすさを大幅に低下させます。

まず、ワーカースレッドを使用することをお勧めします。特定の長時間実行されるタスクを分離して、別々のスレッドが1ブロックの不変入力を受け取り、終了したらメインスレッドのメッセージキューに結果をエンキューします(もちろんこれは同期する必要があります)。メインスレッドは、何もする必要がなければ、シグナルをポーリングまたは明示的に待つかもしれません。特定の詳細は、パフォーマンス要件とアプリケーション設計によって異なります。

長期的には、データベース実装のACIDトランザクションモデルの実装やメッセージの同時実行性を理解してください。たとえば、Erlang言語はあなたに多くのインスピレーションを与えます。これらはスケーラビリティがあることが証明されている同期モデルです。それらの機能を最大限に活用し、これらの概念をあなたが作業しているマルチスレッドアプリケーションに適用しようとします。

0

パフォーマンスがあまり問題にならない場合は、1つのスレッドを使用せずにすべての「同期」を安全に削除できます。すべてを同期すると、何かが、はるかに複雑でエラーが発生しやすい方法です。

なぜあなたは複数のスレッドを使用していますか?