なぜダブルチェックロックシングルトンは、揮発性のキーワードを使用するには?

序文

Javaのメモリモデルから、並行プログラミングの原子と結合し、可視性、大まかに言えば、揮発性の原則の観点から揮発し、コンパイルの役割を考慮の三点を発注、キーワードアプリケーションシナリオを記述。揮発性の問題の下にその分析は、ロックをダブルチェックを回避する方法です。このサプリメントでシングルトンパターンで表示されます。

三つの条件の並行プログラミング

1、アトミック:アトミックの多くを達成する方法、同期可能な、ロックするロック、のAtomicIntegerなど、揮発性のキーワードは原子性を保証することはできません。
2.可視性:可視性を実現するためには、また、ロックを同期させることができます視認性を確保するために使用される揮発性のキーワード;
3、整然とした:コマンドを並べ替え回避するために、同期、ロック機能ブロックの実行順序が自然である、揮発性のキーワードが効果的に達成するために、命令の並べ替え、プログラムの実行を禁止します秩序;

ダブルチェックロッキング

ダブルチェックロッキング(ダブルチェックロック)モードは、多くの場合、フレームワークのソースコードの一部で発生し、目的は、可変遅延を初期化することです。このモードはまた、単一の実施形態を作成するために使用することができます。のは、春のダブルチェックロッキングの例を見てみましょう。
なぜダブルチェックロックシングルトンは、揮発性のキーワードを使用するには?

この場合、あなたは、リソースの読み取りが必要とhandlerMappingsのリアルタイムに時間がかかり、そのアクションであるので、handlerMappingsにコンフィギュレーションファイルをロードする必要があります。私たちは、handlerMappings揮発性使用の前で見ることができます。いくつかは、揮発性の必要がある理由エヴァーだろうか?ダブルチェックロッキングの原則の前の理解が、それは揮発性使用する変数を無視します。
ここでは、この背後にある理由を見てください。

レイジー初期化エラーの例

可変遅延考えを初期化し、最も簡単な例は、決意の変数のために採取されます。
なぜダブルチェックロックシングルトンは、揮発性のキーワードを使用するには?

这个例子在单线程环境可以正常运行,但是在多线程环境就有可能会抛出空指针异常。为了防止这种情况,我们需要在该方法上使用 synchronized。这样该方法在多线程环境就是安全的,但是这么做就会导致每次方法调用都需要获取与释放锁,开销很大。
深入分析可以得知只有在初始化的变量的需要真正加锁,一旦初始化之后,直接返回对象即可。
所以我们可以将该方法改造以下的样子。
なぜダブルチェックロックシングルトンは、揮発性のキーワードを使用するには?

这个方法首先判断变量是否被初始化,没有被初始化,再去获取锁。获取锁之后,再次判断变量是否被初始化。第二次判断目的在于有可能其他线程获取过锁,已经初始化改变量。第二次检查还未通过,才会真正初始化变量。
这个方法检查判定两次,并使用锁,所以形象称为双重检查锁定模式。
这个方案缩小锁的范围,减少锁的开销,看起来很完美。然而这个方案有一些问题却很容易被忽略。

new 实例背后的指令

这个被忽略的问题在于 Cache cache=new Cache()这行代码并不是一个原子指令。使用 javap -c指令,可以快速查看字节码。
なぜダブルチェックロックシングルトンは、揮発性のキーワードを使用するには?

从字节码可以看到创建一个对象实例,可以分为三步:
分配对象内存
调用构造器方法,执行初始化
将对象引用赋值给变量。
虚拟机实际运行时,以上指令可能发生重排序。以上代码 2,3 可能发生重排序,但是并不会重排序 1 的顺序。也就是说 1 这个指令都需要先执行,因为 2,3 指令需要依托 1 指令执行结果。
Java 语言规规定了线程执行程序时需要遵守 intra-thread semantics。intra-thread semantics 保证重排序不会改变单线程内的程序执行结果。这个重排序在没有改变单线程程序的执行结果的前提下,可以提高程序的执行性能。
虽然重排序并不影响单线程内的执行结果,但是在多线程的环境就带来一些问题。
なぜダブルチェックロックシングルトンは、揮発性のキーワードを使用するには?

上面错误双重检查锁定的示例代码中,如果线程 1 获取到锁进入创建对象实例,这个时候发生了指令重排序。当线程1 执行到 t3 时刻,线程 2 刚好进入,由于此时对象已经不为 Null,所以线程 2 可以自由访问该对象。然后该对象还未初始化,所以线程 2 访问时将会发生异常。

volatile 作用

正确的双重检查锁定模式需要需要使用 volatile。volatile主要包含两个功能。
保证可见性。使用 volatile定义的变量,将会保证对所有线程的可见性。
禁止指令重排序优化。
由于 volatile禁止对象创建时指令之间重排序,所以其他线程不会访问到一个未初始化的对象,从而保证安全性。
注意,volatile禁止指令重排序在 JDK 5 之后才被修复

使用局部变量优化性能

重新查看 Spring 中双重检查锁定代码。
なぜダブルチェックロックシングルトンは、揮発性のキーワードを使用するには?

この方法は、内部のローカル変数を参照することができ、最初の値は、インスタンス変数、ローカル変数は、裁判官に割り当てられます。最後に、最初のローカル変数の内容を書き込み、その後、インスタンス変数、ローカル変数を割り当てます。
ローカル変数は、ローカル変数に対して使用されていない、性能を向上させることができます。これは主にあなたがオブジェクトを作成する必要がvolatile変数にいくつかの追加のアクションを必要とする命令の並べ替えを、禁止します。

概要

並べ替えが発生する可能性があり、オブジェクトの指示の作成、マルチスレッド環境でのシステムのセキュリティを確保するために、命令の揮発性の並べ替えの使用を禁止することができます。

遂に

サポートのおかげで、ヨーヨーの記事を覚えて賞賛のポイントのように、みんなと共有へようこそ!

おすすめ

転載: blog.51cto.com/14442094/2429906