ダブルロック
3025 ワード
デュアルロックは英語でDouble-checked Lockingとも呼ばれ、実際の開発過程でマルチスレッド単例モードでの遅延ロード需要に対して提案された方案である.
次は基本的な単一モードです.
システムの起動速度を向上させるために,遅延ロード技術を用いることにし,これを改良した.
クラスはマルチスレッド環境で動作する必要があり、改善されました.
このようなコードは正常に動作していますが、その性能はあまり楽観的ではありません.なぜですか.なぜならgetInstance()メソッドはメソッド全体が同期しているため、アクセス速度が制限されています.最も重要なのは、同期を増加させる鍵は、同期がオブジェクトを最初に初期化するだけで、その後の取得には同期ロックを必要としません.そこで誰かがさらに改善しました
今回の改良では,同期をメソッド内に配置し,初期化を初めて呼び出すときにのみ同期コードブロックに入り,その後,同期ロックを巧みに回避した.皆さんはこのようなコードに問題があると思いますか?
残念なことに、このコードには隠れたエラーがあり、このような書き方はいくつかのプラットフォームと最適化コンパイラでいくつかの問題を引き起こす可能性があります.
なぜなら、オブジェクトを初期化するプロセスは原子操作ではないからです.
aInstance=new DoubleCheckedLock();一部のコンパイラでは、次のように処理できます.
1,aInstantce=新しく割り当てられたメモリアドレス.
2,DoubleCheckedLockのコンストラクション関数を実行して変数初期化を行う.
問題は、スレッドAが最初のステップを実行したばかりでCPU時間が切れた場合、スレッドBが最初のaInstance==nullを判断すると成立しません.メモリアドレスがあるので、直接このオブジェクトに戻りますが、このオブジェクトはまだ合理的に初期化されていません.これにより、隠れたエラーが発生します.
解決策はありますか?
シナリオ1:フラグビットを使用して、オブジェクトの初期化が完了していないことを他のプロセスに誤用されないようにする
この方法は個人的に考えられていますが、もし不適切な点があれば、指摘してください.
次は基本的な単一モードです.
package cn.fsf.thread;
public class DoubleCheckedLock {
private static DoubleCheckedLock aInstance = new DoubleCheckedLock();
public static DoubleCheckedLock getInstance() {
return aInstance;
}
}
システムの起動速度を向上させるために,遅延ロード技術を用いることにし,これを改良した.
package cn.fsf.thread;
public class DoubleCheckedLock {
private static DoubleCheckedLock aInstance;
public static DoubleCheckedLock getInstance() {
if (aInstance == null) {
aInstance = new DoubleCheckedLock();
}
return aInstance;
}
}
クラスはマルチスレッド環境で動作する必要があり、改善されました.
package cn.fsf.thread;
public class DoubleCheckedLock {
private static DoubleCheckedLock aInstance;
public static synchronized DoubleCheckedLock getInstance() {
if (aInstance == null) {
aInstance = new DoubleCheckedLock();
}
return aInstance;
}
}
このようなコードは正常に動作していますが、その性能はあまり楽観的ではありません.なぜですか.なぜならgetInstance()メソッドはメソッド全体が同期しているため、アクセス速度が制限されています.最も重要なのは、同期を増加させる鍵は、同期がオブジェクトを最初に初期化するだけで、その後の取得には同期ロックを必要としません.そこで誰かがさらに改善しました
package cn.fsf.thread;
public class DoubleCheckedLock {
private static DoubleCheckedLock aInstance;
public static DoubleCheckedLock getInstance() {
if (aInstance == null) {
synchronized (DoubleCheckedLock.class) {
if (aInstance == null) {
aInstance = new DoubleCheckedLock();
}
}
}
return aInstance;
}
}
今回の改良では,同期をメソッド内に配置し,初期化を初めて呼び出すときにのみ同期コードブロックに入り,その後,同期ロックを巧みに回避した.皆さんはこのようなコードに問題があると思いますか?
残念なことに、このコードには隠れたエラーがあり、このような書き方はいくつかのプラットフォームと最適化コンパイラでいくつかの問題を引き起こす可能性があります.
なぜなら、オブジェクトを初期化するプロセスは原子操作ではないからです.
aInstance=new DoubleCheckedLock();一部のコンパイラでは、次のように処理できます.
1,aInstantce=新しく割り当てられたメモリアドレス.
2,DoubleCheckedLockのコンストラクション関数を実行して変数初期化を行う.
問題は、スレッドAが最初のステップを実行したばかりでCPU時間が切れた場合、スレッドBが最初のaInstance==nullを判断すると成立しません.メモリアドレスがあるので、直接このオブジェクトに戻りますが、このオブジェクトはまだ合理的に初期化されていません.これにより、隠れたエラーが発生します.
解決策はありますか?
シナリオ1:フラグビットを使用して、オブジェクトの初期化が完了していないことを他のプロセスに誤用されないようにする
package cn.fsf.thread;
public class DoubleCheckedLock {
private static DoubleCheckedLock aInstance;
private static boolean isInstanced;
public static DoubleCheckedLock getInstance() {
if (!isInstanced) {
synchronized (DoubleCheckedLock.class) {
if (aInstance == null) {
aInstance = new DoubleCheckedLock();
isInstanced = true;
}
}
}
return aInstance;
}
}
この方法は個人的に考えられていますが、もし不適切な点があれば、指摘してください.