Javaでシングルトンパターンを実装する効率的な方法は何ですか?
Javaでシングルトンパターンを実装する効率的な方法は何ですか?
回答:
列挙型を使用します。
public enum Foo {
INSTANCE;
}
ジョシュアブロッホは彼にこのアプローチを説明した効果的なJavaのリローデッドの GoogleのI / O 2008で講演:ビデオへのリンクを。彼のプレゼンテーションのスライド30〜32も参照してください(effective_java_reloaded.pdf)。
シリアライズ可能なシングルトンを実装する正しい方法
public enum Elvis { INSTANCE; private final String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" }; public void printFavorites() { System.out.println(Arrays.toString(favoriteSongs)); } }
編集:「Effective Java」のオンライン部分は言う:
「このアプローチは、パブリックフィールドアプローチと機能的に同等ですが、より簡潔であり、シリアル化機構を無料で提供し、洗練されたシリアル化またはリフレクション攻撃に直面しても、複数のインスタンス化に対して強力な保証を提供します。このアプローチはまだ広く採用されていないので、単一要素の列挙型がシングルトンを実装する最良の方法です。」
使い方に応じて、いくつかの「正しい」答えがあります。
java5以来、それを行う最良の方法は列挙型を使用することです:
public enum Foo {
INSTANCE;
}
java5以前では、最も単純なケースは次のとおりです。
public final class Foo {
private static final Foo INSTANCE = new Foo();
private Foo() {
if (INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return INSTANCE;
}
public Object clone() throws CloneNotSupportedException{
throw new CloneNotSupportedException("Cannot clone instance of this class");
}
}
コードを見てみましょう。まず、クラスを最終的なものにします。この場合、final
キーワードを使用して、それが最終であることをユーザーに知らせました。次に、コンストラクターをプライベートにして、ユーザーが独自のFooを作成できないようにする必要があります。コンストラクターから例外をスローすると、ユーザーはリフレクションを使用して2番目のFooを作成できなくなります。次にprivate static final Foo
、唯一のインスタンスを保持するフィールドと、public static Foo getInstance()
それを返すメソッドを作成します。Java仕様では、クラスが最初に使用されたときにのみコンストラクターが呼び出されるようになっています。
非常に大きなオブジェクトまたは重い構築コードがあり、インスタンスが必要になる前に使用できる他のアクセス可能な静的メソッドまたはフィールドがある場合は、遅延初期化を使用するだけで済みます。
を使用しprivate static class
てインスタンスをロードできます。コードは次のようになります。
public final class Foo {
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
}
この行private static final Foo INSTANCE = new Foo();
はクラスFooLoaderが実際に使用されたときにのみ実行されるため、遅延インスタンス化が処理され、スレッドセーフであることが保証されます。
オブジェクトをシリアル化できるようにしたい場合は、逆シリアル化によってコピーが作成されないようにする必要があります。
public final class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
@SuppressWarnings("unused")
private Foo readResolve() {
return FooLoader.INSTANCE;
}
}
メソッドreadResolve()
は、オブジェクトがプログラムの前回の実行でシリアル化された場合でも、唯一のインスタンスが返されることを確認します。
免責事項:私はすべての素晴らしい答えを要約し、私の言葉でそれを書きました。
シングルトンを実装する場合、2つのオプションがあります
。1。遅延読み込み
2.早期読み込み
遅延読み込みはビットオーバーヘッドを追加するので(非常に多くの場合正直です)、非常に大きなオブジェクトまたは重い構築コードがあり、インスタンスが必要になる前に使用できる他のアクセス可能な静的メソッドまたはフィールドがある場合にのみ使用します。遅延初期化を使用する必要があります。それ以外の場合は、早期ロードを選択することをお勧めします。
シングルトンを実装する最も簡単な方法は
public class Foo {
// It will be our sole hero
private static final Foo INSTANCE = new Foo();
private Foo() {
if (INSTANCE != null) {
// SHOUT
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return INSTANCE;
}
}
初期にロードされたシングルトンを除き、すべてが良好です。遅延ロードされたシングルトンを試してみましょう
class Foo {
// Our now_null_but_going_to_be sole hero
private static Foo INSTANCE = null;
private Foo() {
if (INSTANCE != null) {
// SHOUT
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
// Creating only when required.
if (INSTANCE == null) {
INSTANCE = new Foo();
}
return INSTANCE;
}
}
これまでのところ良いですが、ヒーローは、ヒーローの多くのインスタンスを必要とする複数の邪悪な糸で一人で戦っている間は生き残ることができません。だから悪マルチスレッドからそれを保護しましょう
class Foo {
private static Foo INSTANCE = null;
// TODO Add private shouting constructor
public static Foo getInstance() {
// No more tension of threads
synchronized (Foo.class) {
if (INSTANCE == null) {
INSTANCE = new Foo();
}
}
return INSTANCE;
}
}
でもヒーローを守るには十分ではありません、本当に!!! これは私たちのヒーローを助けるために私たちができる/すべきベストです
class Foo {
// Pay attention to volatile
private static volatile Foo INSTANCE = null;
// TODO Add private shouting constructor
public static Foo getInstance() {
if (INSTANCE == null) { // Check 1
synchronized (Foo.class) {
if (INSTANCE == null) { // Check 2
INSTANCE = new Foo();
}
}
}
return INSTANCE;
}
}
これは、「ダブルチェックロックイディオム」と呼ばれます。揮発性のステートメントを忘れがちであり、なぜそれが必要なのかを理解するのは困難です。
詳細:http : //www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
今、私たちは邪悪なスレッドについて確信していますが、残酷な連載についてはどうですか?デシリアライゼーション中に新しいオブジェクトが作成されないことを確認する必要があります
class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static volatile Foo INSTANCE = null;
// Rest of the things are same as above
// No more fear of serialization
@SuppressWarnings("unused")
private Object readResolve() {
return INSTANCE;
}
}
メソッドreadResolve()
は、オブジェクトがプログラムの以前の実行でシリアル化された場合でも、唯一のインスタンスが返されることを確認します。
最後に、スレッドとシリアル化に対する十分な保護を追加しましたが、コードはかさばって醜く見えます。私たちのヒーローに変身させましょう
public final class Foo implements Serializable {
private static final long serialVersionUID = 1L;
// Wrapped in a inner static class so that loaded only when required
private static class FooLoader {
// And no more fear of threads
private static final Foo INSTANCE = new Foo();
}
// TODO add private shouting construcor
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
// Damn you serialization
@SuppressWarnings("unused")
private Foo readResolve() {
return FooLoader.INSTANCE;
}
}
はい、これはまったく同じヒーローです:)
行private static final Foo INSTANCE = new Foo();
はクラスFooLoader
が実際に使用されたときにのみ実行されるので、これは遅延インスタンス化を処理します、
スレッドセーフであることが保証されています。
これまでのところ、私たちが行ったすべてを達成するための最善の方法が最善の方法です
public enum Foo {
INSTANCE;
}
内部的には次のように扱われます
public class Foo {
// It will be our sole hero
private static final Foo INSTANCE = new Foo();
}
それでおしまい!シリアル化、スレッド、醜いコードを恐れる必要はもうありません。また、ENUMSシングルトンは遅延初期化されます。
このアプローチは、機能がパブリックフィールドアプローチと同等ですが、より簡潔であり、シリアル化機構を無料で提供し、洗練されたシリアル化またはリフレクション攻撃に直面しても、複数のインスタンス化に対する強力な保証を提供します。このアプローチはまだ広く採用されていませんが、単一要素の列挙型がシングルトンを実装する最良の方法です。
-「Effective Java」のJoshua Bloch
これで、ENUMSがシングルトンを実装する最良の方法と見なされている理由に気付いたかもしれません。忍耐力に感謝します:)
私のブログで更新しました。
serialVersionUID
あり0L
ます。3番目の問題:カスタマイズなし:列挙型によって定義されたクラス固有のwriteObject、readObject、readObjectNoData、writeReplace、およびreadResolveメソッドは、シリアライズおよびデシリアライズ時に無視されます。
Stu Thompsonによって投稿されたソリューションは、Java5.0以降で有効です。ただし、エラーが発生しやすいと思うので、使用しないほうがよいでしょう。
揮発性のステートメントを忘れがちであり、なぜそれが必要なのかを理解するのは困難です。volatileがないと、ダブルチェックされたロックのアンチパターンのため、このコードはスレッドセーフではなくなります。これについて詳しくは、Java Concurrency in Practiceの 16.2.4項を参照してください。つまり、このパターン(Java5.0より前またはvolatileステートメントなし)は、(まだ)正しくない状態のBarオブジェクトへの参照を返す可能性があります。
このパターンは、パフォーマンスの最適化のために考案されました。しかし、これはもう本当の心配事ではありません。次の遅延初期化コードは高速で、さらに重要なことには読みやすくなっています。
class Bar {
private static class BarHolder {
public static Bar bar = new Bar();
}
public static Bar getBar() {
return BarHolder.bar;
}
}
getBar()
。(ifとgetBar
呼ばれている「早すぎる」、あなたは関係なくsingleonsが実装されている方法と同じ問題に直面していないでしょう。)あなたはここで、上記のコードの怠惰なクラスローディングを見ることができます: pastebin.com/iq2eayiR
Java 5以降のスレッドセーフ:
class Foo {
private static volatile Bar bar = null;
public static Bar getBar() {
if (bar == null) {
synchronized(Foo.class) {
if (bar == null)
bar = new Bar();
}
}
return bar;
}
}
編集:volatile
ここの修飾子に注意してください。:)それがなければ、他のスレッドがJMM(Javaメモリモデル)によってその値の変更を確認することが保証されないため、重要です。同期ではその処理は行われず、コードブロックへのアクセスがシリアル化されるだけです。
編集2:@Bnoの回答は、Bill Pugh(FindBugs)が推奨するアプローチの詳細であり、議論の余地があります。読んで彼の答えにも投票してください。
遅延初期化を忘れてください。問題が多すぎます。これが最も簡単な解決策です。
public class A {
private static final A INSTANCE = new A();
private A() {}
public static A getInstance() {
return INSTANCE;
}
}
本当に必要なことを確認してください。「シングルトンアンチパターン」のグーグルを実行して、それに対するいくつかの議論を見てください。本質的に問題はないと思いますが、これはグローバルリソース/データを公開するためのメカニズムにすぎないため、これが最善の方法であることを確認してください。特に、DIを使用するとモックされたリソースをテスト目的で使用できるため、単体テストも使用している場合は特に、依存性注入がより有用であることがわかりました。
シングルトンを使用する代わりにDIを提案するいくつかの回答に私は困惑しています。これらは無関係な概念です。DIを使用して、シングルトンまたは非シングルトン(スレッドごとなど)のインスタンスを注入できます。Spring 2.xを使用している場合、少なくともこれは当てはまります。他のDIフレームワークについて話すことはできません。
したがって、OPに対する私の答えは(最も簡単なサンプルコードを除いて)次のようになります。
このアプローチは、シングルトンを使用するかどうかが簡単にリバーシブルな実装の詳細である(もちろん、使用するシングルトンがスレッドセーフである場合)、優れた分離された(したがって、柔軟でテスト可能な)アーキテクチャを提供します。
TicketNumberer
するには、単一のグローバルインスタンスを必要とするとTicketIssuer
、コード行を含むクラスをどこに書き込むかを検討しますint ticketNumber = ticketNumberer.nextTicketNumber();
。従来のシングルトンの考え方では、前のコード行はのようにする必要がありますTicketNumberer ticketNumberer = TicketNumberer.INSTANCE;
。DIの考え方では、クラスにはのようなコンストラクタがありpublic TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }
ます。
main
メソッド(またはそのミニオンの1つ)が依存関係を作成してからコンストラクターを呼び出すため、手作りのDIアーキテクチャがそれを行います。基本的に、グローバル変数(またはグローバルメソッド)の使用は、恐ろしいサービスロケーターパターンの単純な形式であり、そのパターンの他の使用と同様に、依存関係注入に置き換えることができます。
書く前に、なぜシングルトンが必要なのかをよく考えてください。Javaでシングルトンをグーグルする場合、簡単につまずくことができる、それらを使用することについての準宗教的な議論があります。
個人的には、多くの理由からシングルトンをできるだけ避けようとしますが、そのほとんどはシングルトンをグーグルすることで見つけることができます。シングルトンは、誰でも簡単に理解でき、「グローバルな」データをOO設計に取り込むためのメカニズムとして使用され、オブジェクトのライフサイクル管理を回避しやすいために使用されている(または、 AをBの中からどのように実行できるかを本当に考えています。制御の反転(IoC)や依存性注入(DI)などを見て、良い中間点を見つけてください。
本当に必要な場合は、ウィキペディアにシングルトンの適切な実装の良い例があります。
以下は3つの異なるアプローチです
1)列挙型
/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
INSTANCE;
}
2)ダブルチェックされたロック/遅延読み込み
/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
private static volatile DoubleCheckedLockingSingleton INSTANCE;
private DoubleCheckedLockingSingleton(){}
public static DoubleCheckedLockingSingleton getInstance(){
if(INSTANCE == null){
synchronized(DoubleCheckedLockingSingleton.class){
//double checking Singleton instance
if(INSTANCE == null){
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
3)静的ファクトリーメソッド
/**
* Singleton pattern example with static factory method
*/
public class Singleton{
//initailzed during class loading
private static final Singleton INSTANCE = new Singleton();
//to prevent creating another instance of Singleton
private Singleton(){}
public static Singleton getSingleton(){
return INSTANCE;
}
}
バージョン1:
public class MySingleton {
private static MySingleton instance = null;
private MySingleton() {}
public static synchronized MySingleton getInstance() {
if(instance == null) {
instance = new MySingleton();
}
return instance;
}
}
遅延読み込み、ブロッキングでスレッドセーフ、低パフォーマンスsynchronized
。
バージョン2:
public class MySingleton {
private MySingleton() {}
private static class MySingletonHolder {
public final static MySingleton instance = new MySingleton();
}
public static MySingleton getInstance() {
return MySingletonHolder.instance;
}
}
遅延読み込み、スレッドセーフ、ノンブロッキング、高性能。
遅延読み込みが必要ない場合は、単に試してください
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return Singleton.INSTANCE; }
protected Object clone() {
throw new CloneNotSupportedException();
}
}
遅延読み込みが必要で、シングルトンをスレッドセーフにしたい場合は、ダブルチェックパターンを試してください
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if(null == instance) {
synchronized(Singleton.class) {
if(null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
protected Object clone() {
throw new CloneNotSupportedException();
}
}
二重チェックパターンの動作が保証されていないため(コンパイラーの問題のため、それについては何も知りません)、getInstanceメソッド全体を同期するか、すべてのシングルトンのレジストリを作成することもできます。
volatile
イナムシングルトン
Javaで列挙型を使用するシングルトンは、一般的に列挙型シングルトンを宣言する方法です。列挙型シングルトンには、インスタンス変数とインスタンスメソッドを含めることができます。簡単にするために、インスタンスメソッドを使用している場合は、オブジェクトの状態に影響を与える場合にそのメソッドのスレッドセーフを確保する必要があることにも注意してください。
enumの使用は実装が非常に簡単であり、他の方法で回避する必要があるシリアル化可能なオブジェクトに関する欠点はありません。
/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
INSTANCE;
public void execute (String arg) {
//perform operation here
}
}
あなたはでそれにアクセスすることができますSingleton.INSTANCE
はるかに簡単に呼び出すよりも、getInstance()
シングルトンにする方法を。
1.12列挙型定数のシリアル化
列挙定数は、通常の直列化可能または外部化可能オブジェクトとは異なる方法で直列化されます。enum定数のシリアル化された形式は、その名前のみで構成されます。定数のフィールド値はフォームに存在しません。enum定数をシリアル化するに
ObjectOutputStream
は、enum定数のnameメソッドによって返された値を書き込みます。列挙型定数を逆シリアル化するにObjectInputStream
は、ストリームから定数名を読み取ります。次に、java.lang.Enum.valueOf
メソッドを呼び出して、受け取った定数名と共に定数の列挙型を引数として渡すことにより、逆シリアル化された定数を取得します。他の直列化可能オブジェクトまたは外部化可能オブジェクトと同様に、列挙定数は、直列化ストリームに後で現れる後方参照のターゲットとして機能できます。任意のクラス固有の:enum定数が直列化されるプロセスは、カスタマイズすることができない
writeObject
、readObject
、readObjectNoData
、writeReplace
、およびreadResolve
列挙タイプによって定義された方法は、シリアライゼーションおよびデシリアライゼーションの際に無視されます。同様に、任意のserialPersistentFields
orserialVersionUID
フィールド宣言も無視されます。すべての列挙型にはの固定がserialVersionUID
あり0L
ます。送信されるデータのタイプに変化がないため、列挙型のシリアライズ可能なフィールドとデータを文書化する必要はありません。
従来のシングルトンのもう1つの問題は、Serializable
インターフェースを実装すると、readObject()
メソッドが常にJavaのコンストラクターのような新しいインスタンスを返すため、インターフェースがシングルトンのままではなくなることです。これはreadResolve()
、以下のようにシングルトンに置き換えることにより、新しく作成されたインスタンスを使用して破棄することで回避できます
// readResolve to prevent another instance of Singleton
private Object readResolve(){
return INSTANCE;
}
シングルトンクラスが状態を維持する場合、状態を一時的にする必要があるため、これはさらに複雑になる可能性がありますが、列挙型シングルトンでは、JVMによってシリアル化が保証されます。
良い読み
There are 4 ways to create a singleton in java.
1- eager initialization singleton
public class Test{
private static final Test test = new Test();
private Test(){}
public static Test getTest(){
return test;
}
}
2- lazy initialization singleton (thread safe)
public class Test {
private static volatile Test test;
private Test(){}
public static Test getTest() {
if(test == null) {
synchronized(Test.class) {
if(test == null){test = new Test();
}
}
}
return test;
}
3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)
public class Test {
private Test(){}
private static class TestHolder{
private static final Test test = new Test();
}
public static Test getInstance(){
return TestHolder.test;
}
}
4- enum singleton
public enum MySingleton {
INSTANCE;
private MySingleton() {
System.out.println("Here");
}
}
これについては少し遅れるかもしれませんが、シングルトンの実装には多くの微妙な違いがあります。ホルダーパターンは多くの状況で使用できません。volatileを使用する場合はIMO-ローカル変数も使用する必要があります。最初から始めて、問題を繰り返しましょう。私の言っていることがわかります。
最初の試みは次のようになります。
public class MySingleton {
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new MySingleton();
}
return INSTANCE;
}
...
}
ここには、INSTANCEというプライベート静的メンバーを持つgetSingletonクラスと、getInstance()というパブリック静的メソッドがあります。最初にgetInstance()が呼び出されたとき、INSTANCEメンバーはnullです。その後、フローは作成条件に該当し、MySingletonクラスの新しいインスタンスを作成します。その後getInstance()を呼び出すと、INSTANCE変数がすでに設定されているため、別のMySingletonインスタンスは作成されません。これにより、getInstance()のすべての呼び出し元で共有されるMySingletonのインスタンスが1つだけ存在することが保証されます。
しかし、この実装には問題があります。マルチスレッドアプリケーションは、単一インスタンスの作成時に競合状態になります。複数の実行スレッドが同時に(またはその前後で)getInstance()メソッドにヒットした場合、それぞれのスレッドはINSTANCEメンバーをnullとして認識します。これにより、各スレッドで新しいMySingletonインスタンスが作成され、その後INSTANCEメンバーが設定されます。
private static MySingleton INSTANCE;
public static synchronized MySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new MySingleton();
}
return INSTANCE;
}
ここでは、メソッドシグネチャでsynchronizedキーワードを使用して、getInstance()メソッドを同期しています。これは確かに私たちの競争状態を修正します。スレッドはブロックされ、一度に1つずつメソッドに入ります。ただし、パフォーマンスの問題も発生します。この実装は、単一インスタンスの作成を同期するだけでなく、読み取りを含むgetInstance()へのすべての呼び出しを同期します。読み取りは単にINSTANCEの値を返すため、同期する必要はありません。読み取りは呼び出しの大部分を占めるため(インスタンス化は最初の呼び出しでのみ行われることに注意してください)、メソッド全体を同期することにより、不要なパフォーマンスヒットが発生します。
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronize(MySingleton.class) {
INSTANCE = new MySingleton();
}
}
return INSTANCE;
}
ここでは、同期をメソッドシグネチャからMySingletonインスタンスの作成をラップする同期ブロックに移動しました。しかし、これで問題は解決しますか?さて、読み取りをブロックすることはなくなりましたが、一歩後退しました。複数のスレッドが同時にまたはほぼ同時にgetInstance()メソッドにヒットし、それらすべてがINSTANCEメンバーをnullとして認識します。次に、同期ブロックにヒットして、ロックを取得し、インスタンスを作成します。そのスレッドがブロックを出ると、他のスレッドがロックを求めて競合し、各スレッドが1つずつブロックを通り抜けて、クラスの新しいインスタンスを作成します。だから、私たちは私たちが始めたところに戻っています。
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronized(MySingleton.class) {
if (INSTANCE == null) {
INSTANCE = createInstance();
}
}
}
return INSTANCE;
}
ここでは、ブロック内から別のチェックを発行します。INSTANCEメンバーが既に設定されている場合は、初期化をスキップします。これは、ダブルチェックロックと呼ばれます。
これにより、複数のインスタンス化の問題が解決されます。しかし、もう一度、私たちのソリューションは別の課題を提示しました。他のスレッドは、INSTANCEメンバーが更新されたことを「認識」しない場合があります。これは、Javaがメモリ操作を最適化する方法が原因です。スレッドは、変数の元の値をメインメモリからCPUのキャッシュにコピーします。値の変更は、そのキャッシュに書き込まれ、そのキャッシュから読み取られます。これは、パフォーマンスを最適化するように設計されたJavaの機能です。しかし、これはシングルトンの実装に問題を引き起こします。2番目のスレッド(別のCPUまたはコアによって処理され、別のキャッシュを使用)は、最初のスレッドによる変更を認識しません。これにより、2番目のスレッドはINSTANCEメンバーをnullとして認識し、シングルトンの新しいインスタンスを強制的に作成します。
private static volatile MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronized(MySingleton.class) {
if (INSTANCE == null) {
INSTANCE = createInstance();
}
}
}
return INSTANCE;
}
これを解決するには、INSTANCEメンバーの宣言でvolatileキーワードを使用します。これにより、CPUキャッシュではなく、メインメモリから常に読み取りと書き込みを行うようコンパイラーに指示します。
しかし、この単純な変更には代償が伴います。CPUキャッシュをバイパスしているため、揮発性のINSTANCEメンバーを操作するたびにパフォーマンスが低下します。これは4回実行されます。存在(1と2)を再確認し、値(3)を設定してから、値(4)を返します。メソッドの最初の呼び出し中にのみインスタンスを作成するので、このパスはフリンジケースであると主張することができます。おそらく、作成時のパフォーマンスへの影響は許容できるでしょう。しかし、私たちの主なユースケースである読み取りでさえ、揮発性メンバーを2回操作します。一度存在を確認し、もう一度その値を返します。
private static volatile MySingleton INSTANCE;
public static MySingleton getInstance() {
MySingleton result = INSTANCE;
if (result == null) {
synchronized(MySingleton.class) {
result = INSTANCE;
if (result == null) {
INSTANCE = result = createInstance();
}
}
}
return result;
}
パフォーマンスヒットはvolatileメンバーを直接操作することによるものなので、ローカル変数をvolatileの値に設定し、代わりにローカル変数を操作してみましょう。これにより、揮発性の操作回数が減り、失われたパフォーマンスの一部を取り戻すことができます。同期ブロックに入るときに、ローカル変数を再度設定する必要があることに注意してください。これにより、ロックを待機している間に発生したすべての変更が最新の状態になります。
最近これについて記事を書きました。シングルトンの分解。これらの例の詳細と「ホルダー」パターンの例がそこにあります。また、ダブルチェックされた揮発性アプローチを紹介する実際の例もあります。お役に立てれば。
class MySingleton
–多分それはそうあるべきfinal
ですか?
BearerToken
インスタンスはBearerTokenFactory
特定の認証サーバーで構成されている-の一部であるため、静的ではありません。多くのBearerTokenFactory
オブジェクトが存在する可能性があります-それぞれが独自の「キャッシュ」BearerToken
を持ち、期限が切れるまで渡されます。のhasExpired()
メソッドBeraerToken
はファクトリのget()
メソッドで呼び出され、期限切れのトークンを渡さないようにします。有効期限が切れている場合、新しいトークンが認証サーバーに要求されます。コードブロックに続く段落で、これについて詳しく説明します。
これはシンプルな実装方法singleton
です:
public class Singleton {
// It must be static and final to prevent later modification
private static final Singleton INSTANCE = new Singleton();
/** The constructor must be private to prevent external instantiation */
private Singleton(){}
/** The public static method allowing to get the instance */
public static Singleton getInstance() {
return INSTANCE;
}
}
これはあなたを適切に遅延作成する方法singleton
です:
public class Singleton {
// The constructor must be private to prevent external instantiation
private Singleton(){}
/** The public static method allowing to get the instance */
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* The static inner class responsible for creating your instance only on demand,
* because the static fields of a class are only initialized when the class
* is explicitly called and a class initialization is synchronized such that only
* one thread can perform it, this rule is also applicable to inner static class
* So here INSTANCE will be created only when SingletonHolder.INSTANCE
* will be called
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
getInstance()
。しかし実際、クラスに他の静的メソッドSingleton
がなく、呼び出すだけの場合getInstance()
、実際の違いはありません。
あなたは必要とするダブルチェックあなたが遅延したクラスのインスタンス変数をロードする必要がある場合イディオムを。静的変数またはシングルトンをレイジーにロードする必要がある場合は、オンデマンドホルダーイディオムでの初期化が必要です。
さらに、シングルトンをシリアライズ可能にする必要がある場合、他のすべてのフィールドを一時的である必要があり、シングルトンオブジェクトを不変に維持するためにreadResolve()メソッドを実装する必要があります。それ以外の場合は、オブジェクトが逆シリアル化されるたびに、オブジェクトの新しいインスタンスが作成されます。readResolve()が行うことは、readObject()によって読み取られた新しいオブジェクトを置き換えることです。これにより、その新しいオブジェクトを参照する変数がないため、その新しいオブジェクトはガベージコレクションされます。
public static final INSTANCE == ....
private Object readResolve() {
return INSTANCE; // original singleton instance.
}
シングルトンオブジェクトを作成するさまざまな方法:
Joshua Blochによると-Enumが最高でしょう。
ダブルチェックロックも使用できます。
内部静的クラスも使用できます。
列挙型シングルトン
スレッドセーフなシングルトンを実装する最も簡単な方法は、列挙型を使用することです
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
System.out.println("This is a singleton");
}
}
このコードは、Java 1.5のEnumの導入以来機能します。
ダブルチェックロック
マルチスレッド環境(Java 1.5以降)で機能する「クラシック」シングルトンをコーディングする場合は、これを使用する必要があります。
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
}
1.5以前は、volatileキーワードの実装が異なるため、これはスレッドセーフではありません。
シングルトンの早期ロード(Java 1.5より前でも機能)
この実装は、クラスが読み込まれたときにシングルトンをインスタンス化し、スレッドセーフを提供します。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
public void doSomething(){
System.out.println("This is a singleton");
}
}
シングルトンに対してよく使用される別の引数は、テスト容易性の問題です。シングルトンは、テスト目的で簡単にモック可能ではありません。これが問題であることが判明した場合は、次のわずかな変更を加えます。
public class SingletonImpl {
private static SingletonImpl instance;
public static SingletonImpl getInstance() {
if (instance == null) {
instance = new SingletonImpl();
}
return instance;
}
public static void setInstance(SingletonImpl impl) {
instance = impl;
}
public void a() {
System.out.println("Default Method");
}
}
追加されたsetInstance
メソッドにより、テスト中にシングルトンクラスのモックアップ実装を設定できます。
public class SingletonMock extends SingletonImpl {
@Override
public void a() {
System.out.println("Mock Method");
}
}
これは初期の初期化アプローチでも機能します。
public class SingletonImpl {
private static final SingletonImpl instance = new SingletonImpl();
private static SingletonImpl alt;
public static void setInstance(SingletonImpl inst) {
alt = inst;
}
public static SingletonImpl getInstance() {
if (alt != null) {
return alt;
}
return instance;
}
public void a() {
System.out.println("Default Method");
}
}
public class SingletonMock extends SingletonImpl {
@Override
public void a() {
System.out.println("Mock Method");
}
}
これには、この機能を通常のアプリケーションに公開するという欠点もあります。そのコードに取り組んでいる他の開発者は、「setInstance」メソッドを使用して特定の関数を変更し、アプリケーション全体の動作を変更するように誘惑される可能性があります。
それでも、モックアップテストの可能性については(必要な場合)、このコードの露出は許容できる代償となる可能性があります。
最も単純なシングルトンクラス
public class Singleton {
private static Singleton singleInstance = new Singleton();
private Singleton() {}
public static Singleton getSingleInstance() {
return singleInstance;
}
}
この投稿をご覧ください。
ベストアンサーの「シングルトン」セクションから、
シングルトン(毎回同じインスタンス(通常はそれ自体)を返す作成メソッドで認識可能)
- java.lang.Runtime#getRuntime()
- java.awt.Desktop#getDesktop()
- java.lang.System#getSecurityManager()
Javaネイティブクラス自体からシングルトンの例を学ぶこともできます。
私が今まで見た中で最高のシングルトンパターンは、サプライヤーインターフェイスを使用しています。
下記参照:
public class Singleton<T> implements Supplier<T> {
private boolean initialized;
private Supplier<T> singletonSupplier;
public Singleton(T singletonValue) {
this.singletonSupplier = () -> singletonValue;
}
public Singleton(Supplier<T> supplier) {
this.singletonSupplier = () -> {
// The initial supplier is temporary; it will be replaced after initialization
synchronized (supplier) {
if (!initialized) {
T singletonValue = supplier.get();
// Now that the singleton value has been initialized,
// replace the blocking supplier with a non-blocking supplier
singletonSupplier = () -> singletonValue;
initialized = true;
}
return singletonSupplier.get();
}
};
}
@Override
public T get() {
return singletonSupplier.get();
}
}
単純な「static Foo foo = new Foo();
」では不十分な場合があります。実行したい基本的なデータ挿入について考えてみてください。
一方、シングルトン変数をインスタンス化するメソッドを同期する必要があります。同期自体は悪くありませんが、パフォーマンスの問題やロックを引き起こす可能性があります(この例を使用する非常にまれな状況では。解決策は
public class Singleton {
private static Singleton instance = null;
static {
instance = new Singleton();
// do some of your instantiation stuff here
}
private Singleton() {
if(instance!=null) {
throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
}
}
public static getSingleton() {
return instance;
}
}
今、何が起こりますか?クラスはクラスローダーを介してロードされます。クラスがバイト配列から解釈された直後に、VMは静的{} -ブロックを実行します。それが全体の秘密です:static-blockは、指定されたパッケージの指定されたクラス(名前)がこの1つのクラスローダーによってロードされるときに一度だけ呼び出されます。
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton(){
if (INSTANCE != null)
throw new IllegalStateException (“Already instantiated...”);
}
public synchronized static Singleton getInstance() {
return INSTANCE;
}
}
getInstanceの前にSynchronizedキーワードを追加したため、2つのスレッドが同時にgetInstanceを呼び出す場合の競合状態を回避しました。