synchronized
キーワードを使用せずにコードをスレッドセーフにするための可能な方法は何ですか?
7 に答える
実際には、多くの方法があります。
- 可変状態がない場合は、同期の必要はまったくありません。
- 可変状態がシングルスレッドに限定されている場合は、同期の必要はありません。これは、ローカル変数またはを使用して実行できます
java.lang.ThreadLocal
。 - 組み込みのシンクロナイザーを使用することもできます。
java.util.concurrent.locks.ReentrantLock
ブロックやメソッドを使用するときにアクセスするロックと同じ機能があり、synchronized
さらに強力です。
メソッドに対してローカルな変数/参照のみを持ちます。または、すべてのインスタンス変数が不変であることを確認してください。
すべてのデータを不変にすることで、コードをスレッドセーフにすることができます。可変性がなければ、すべてがスレッドセーフです。
次に、多数のリーダーと少数のライターが存在する場合により優れたパフォーマンスを発揮する読み取り/書き込みロックを提供するためのプロビジョニングを備えた Java 並行 API を確認することをお勧めします。純粋な同期キーワードも 2 つのリーダーをブロックします。
////////////FIRST METHOD USING SINGLE boolean//////////////
public class ThreadTest implements Runnable {
ThreadTest() {
Log.i("Ayaz", "Constructor..");
}
private boolean lockBoolean = false;
public void run() {
Log.i("Ayaz", "Thread started.." + Thread.currentThread().getName());
while (lockBoolean) {
// infinite loop for other thread if one is accessing
}
lockBoolean = true;
synchronizedMethod();
}
/**
* This method is synchronized without using synchronized keyword
*/
public void synchronizedMethod() {
Log.e("Ayaz", "processing...." + Thread.currentThread().getName());
try {
Thread.currentThread().sleep(3000);
} catch (Exception e) {
System.out.println("Exp");
}
Log.e("Ayaz", "complete.." + Thread.currentThread().getName());
lockBoolean = false;
}
} //end of ThreadTest class
//For testing use below line in main method or in Activity
ThreadTest threadTest = new ThreadTest();
Thread threadA = new Thread(threadTest, "A thead");
Thread threadB = new Thread(threadTest, "B thead");
threadA.start();
threadB.start();
///////////SECOND METHOD USING TWO boolean/////////////////
public class ThreadTest implements Runnable {
ThreadTest() {
Log.i("Ayaz", "Constructor..");
}
private boolean isAnyThreadInUse = false;
private boolean lockBoolean = false;
public void run() {
Log.i("Ayaz", "Thread started.." + Thread.currentThread().getName());
while (!lockBoolean)
if (!isAnyThreadInUse) {
isAnyThreadInUse = true;
synchronizedMethod();
lockBoolean = true;
}
}
/**
* This method is synchronized without using synchronized keyword
*/
public void synchronizedMethod() {
Log.e("Ayaz", "processing...." + Thread.currentThread().getName());
try {
Thread.currentThread().sleep(3000);
} catch (Exception e) {
System.out.println("Exp");
}
Log.e("Ayaz", "complete.." + Thread.currentThread().getName());
isAnyThreadInUse = false;
}
} // end of ThreadTest class
//For testing use below line in main method or in Activity
ThreadTest threadTest = new ThreadTest();
Thread t1 = new Thread(threadTest, "a thead");
Thread t2 = new Thread(threadTest, "b thead");
t1.start();
t2.start();
予測可能性を維持するには、変更可能なデータへのすべてのアクセスが順次行われるようにするか、並列アクセスによって引き起こされる問題を処理する必要があります。
最も粗大な保護は、synchronized
キーワードを使用します。さらに、少なくとも 2 つの可能性があり、それぞれにメリットがあります。
ロック/セマフォ
これらは非常に効果的です。たとえば、多くのスレッドによって読み取られ、更新されるのは 1 つのスレッドだけである構造がある場合、 がReadWriteLock
役立つことがあります。
アルゴリズムに一致するロックを選択すると、ロックがはるかに効率的になります。
アトミック
AtomicReference
たとえば、を使用すると、多くの場合、完全にロックのない機能を提供できます。これは通常、大きなメリットをもたらします。
アトミックの背後にある理由は、それらが失敗することを許可することですが、それを処理できる方法で失敗したことを伝えることです。
たとえば、値を変更したい場合は、それを読み取ってから、古い値のままである限り、新しい値を書き込むことができます。これは「比較と設定」と呼ばれ、cas
通常はハードウェアで実装できるため、非常に効率的です。次に必要なのは、次のようなものだけです。
long old = atomic.get();
while ( !atomic.cas(old, old+1) ) {
// The value changed between my get and the cas. Get it again.
old = atomic.get();
}
ただし、予測可能性は常に要件ではないことに注意してください。
なぜそれをする必要があるのですか?
ローカル変数/参照のみを使用しても、複雑なビジネス ニーズのほとんどは解決されません。また、インスタンス変数が不変の場合、それらの参照は他のスレッドによって変更される可能性があります。
1 つのオプションはSingleThreadModelのようなものを使用することですが、これは推奨されておらず、非推奨です。
上記でKalが提案したように、並行APIを見ることもできます