私の教科書の 1 つは、synchronized() の引数はこれでなければならないと述べています... 私はそれが間違っていることを知っています。しかし、synchronized(this) の方が安全なので、常に使用する必要があると聞きました。本当 ?ありがとうございました:)
8 に答える
いいえ、常にこれである必要はありません。また、これがないため、静的メソッドの場合は単純にできません。
また、これに同期するのは間違っていると見なされることもあります。これは、ロック オブジェクトが外部から見えるためです。
public class Example {
private final Object lock = new Object();
// does not compile, there is no 'this' in static context.
public static void staticMethod() {
synchronized (this) {
}
}
public void method() {
int x = 3;
//there is risk that someone else outside our code
//uses same lock
synchronized (this) {
}
//this lock is private
synchronized (lock) {
}
}
}
私の教科書の 1 つは、synchronized() の引数はこれでなければならないと述べています... 私はそれが間違っていることを知っています。
それは正しくありません。教科書が間違っているか、あなたがそれを誤解しています。Java 言語を使用すると、任意の(null 以外の) オブジェクト参照を同期できます。
しかし、synchronized(this) の方が安全なので、常に使用する必要があると聞きました。本当?
いいえ、それも真実ではありません。安全ではありませんし、常にロックオンするべきではありませんthis
。
実際、「それ自体」をロックする必要がある汎用ライブラリ クラスを作成している場合は、プライベート ロック フィールドを宣言することをお勧めします。例えば
private final Object myLock = new Object();
...ではなくそのオブジェクトをロックしますthis
。これにより、外部コードがなんらかの理由でライブラリ オブジェクトをロックすることを決定し、ライブラリ クラス メソッドと外部コードの間で不要な競合やデッドロックが発生する可能性があるという問題が解消されます。
教科書が言おうとしていたのは、プリミティブ ロックを使用してデータ構造の相互排除と同期を実現するすべてのメソッドは、正しいオブジェクトをロックとして使用する必要があるということだと思います。これは必ずしもデータ構造オブジェクト自体ではありませんが、そのオブジェクトを意味する必要があります... ある意味で。(データ構造を示すオブジェクトをロックしないと、データ構造を使用/更新している間、あるスレッドが他のスレッドを除外しないリスクがあります。)
これは、プライベート ロックが回避しようとしている問題の概要です。
/** This class implements thread-safe getting / setting by synchronizing on 'this' */
public class IntHolder {
private int value;
public int getValue() {
synchronized(this) { return value; }
}
public void setValue(int value)
synchronized(this) { this.value = value; }
}
/* Somewhere else, some other code (the "external code") used a holder instance
as the lock for some larger-scale synchronization. */
IntHolder h = ...
synchronized (h) {
/* Do something that takes a long time ... */
}
問題は、外部コードがそのロックを保持している間h
、他のスレッドがホルダーの値を読み取ったり変更したりできないことです。それが意図されたものであるなら、それでいいのです。ただし、IntHolder
型のスレッドセーフが「単なる実装の詳細」であることを意図している場合、予期しない失敗のケースが発生する可能性があります。
ああ、悪名高いJavaで同期(これ)を避けますか?。
実際には、違いはありません
public synchronized void doThis() {
}
と
public void doThis() {
synchronized (this) {
}
}
バイトコードレベルを除く。どちらもスレッドセーフに対応しています。(たとえば、同期されたブロック内で同じクラスのロックを設定した場合)デッドロックが発生する可能性があるため、どちらも問題を引き起こします。
デッドロックが心配な場合は、次のlock
ように専用を使用する必要があります。
public class MyClass {
private final Object lock = new Object(); //Must be final
public void doThis() {
synchronized (lock) {
}
}
}
または、Javajava.util.concurrent.Lock
インターフェースとjava.util.concurrent.locks.ReentrantLock
実装を使用して、基本的にロックを実行します。
IMO Synchronized(this) がその特定の場所で良いアイデアかどうかわからない場合は、フィールドとメソッドで「volatile」と「synchronized」を使用することをお勧めします。
簡単に言うと、volatile は組み込みミューテックスを変数に配置します。synchronized は、関数呼び出しに組み込みミューテックスを配置します。同期ブロックは、パラメーターによってロックされたコード部分の周りに組み込みミューテックスを配置します。
通常、オブジェクトをロックしてロックしますが、インスタンス自体をロックしたい場合もあります...ただし、メンバーの1つにのみアクセスする必要がある場合は、クラスインスタンス全体でロックすることに注意してください。 /fields (そもそも Synchronized と volatile を使用して解決できたもの)
これをロックしたい (またはしたくない) 本当の理由は、ロックのスコープです。これをロックすると、ロックが表示されます。プライベート オブジェクトを初期化すると、ロックは隠されます。
非静的メソッドを次のように宣言した場合synchronized
:
public synchronized void doSomething()
{
...
}
で同期されthis
ます。したがって、上記のような同期された非静的メソッドでsynchronized
ブロックを同期することが目標である場合は、 を使用する必要があります。synchronized(this)
しかし、あなたは正しいです:の同期メソッドではなくsynchronized(someOtherObject)
の同期メソッドをロックアウトすることを認識している限り、 を書くこともできます。someOtherObject
this
(ちなみに、静的メソッドの場合、含まれているクラスを表すインスタンスでsynchronized
同期します。)Class
いいえ、必ずしも を使用する必要はありません。this
のインスタンスではない他のオブジェクトにロックを適用してもthis
。
synchronized(obj1){
-------
}
synchronized(obj2){
-------
}
単一のメソッドでは、最初にオブジェクトのロックを取得し、obj1
次に作業を行って解放し、obj2 のロックを取得する上記のようなものを書くことができます。
どのロックを使用してもかまいません。すべての java.lang.Object はロックとして機能できます。同じ変更可能な状態で動作するすべての操作が同じロックを使用していることを確認する必要があります。
とまったくsynchronized(this)
同じくらい安全です
private final Object lock = new Object();
synchronized(lock)
Synchronized の引数は、単にホールドが置かれるオブジェクトです。引数がどうあるべきかを決定する同期ブロックで何をしたいかによって異なります。を使用this
すると、現在のオブジェクトが保持されます。
個人的な逸話として、私が現在取り組んでいるプロジェクトでは、同期されたすべてのホールドが COM ポート上にあるため、パケットの送信と受信の間に衝突はありません。