La manière la plus courante de l'écrire est Non Thread Safe.
public class Singleton1 {
private static Singleton1 singleton = null;
private Singleton1() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static Singleton1 getInstance() {
if (singleton == null) {
singleton = new Singleton1();
}
return singleton;
}
}
Non-Thread-Safe
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<Singleton1> f1 = executor.submit(Singleton1::getInstance);
Future<Singleton1> f2 = executor.submit(Singleton1::getInstance);
Singleton1 s1 = f1.get();
Singleton1 s2 = f2.get();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
ID différent
design.singleton.Singleton1@7ba4f24f
design.singleton.Singleton1@3b9a45b3
false
synchronized
Ajoutez synchronized
au style d'écriture typique pour le rendre Thread Safe
.
Cependant, il existe un problème de performances car le verrou synchronisé ne peut pas être libéré même après la création de l'instance.
synchronized
public class Singleton2 {
private static Singleton2 singleton = null;
private Singleton2() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static synchronized Singleton2 getInstance() {
if (singleton == null) {
singleton = new Singleton2();
}
return singleton;
}
}
Double Check Lock
En ajoutant à nouveau Check au-dessus de synchronized
,
Cela évite le "verrouillage synchronisé".
Cependant, en raison de problèmes d'atomicité, ce n'est pas Thread Safe!
Je ne connais pas les détails, mais c'est parce que new Singleton3 ()
est décomposé en plusieurs parties et exécuté.
DCL
public class Singleton3 {
private static Singleton3 singleton = null;
private Singleton3() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static Singleton3 getInstance() {
//Non thread-safe en raison de l'atomicité
if (singleton == null) {
synchronized (Singleton3.class) {
if (singleton == null) {
singleton = new Singleton3();
}
}
}
return singleton;
}
}
volatile + DCL Il utilise «volatile», garantit la primitivité et implémente Double Check Lock.
volatile
public class Singleton4 {
private static volatile Singleton4 singleton = null;
private Singleton4() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static Singleton4 getInstance() {
//Volatile garantit l'atomicité
if (singleton == null) {
synchronized (Singleton4.class) {
if (singleton == null) {
singleton = new Singleton4();
}
}
}
return singleton;
}
}
static fianl Grâce au chargeur de classe, il est Thread Safe et simple à écrire. Cependant, les ressources peuvent être gaspillées.
static_final
public class Singleton5 {
private static final Singleton5 singleton = new Singleton5();
private Singleton5() {}
public static Singleton5 getInstance() {
return singleton;
}
}
Holder
C'est une implémentation qui rend Thread Safe avec static final
, mais ne gaspille pas de ressources en générant des retards.
Holder
public class Singleton6 {
private static class SingletonHolder {
private static final Singleton6 singleton = new Singleton6();
private SingletonHolder() { }
}
private Singleton6() {
}
public static Singleton6 getInstance() {
return SingletonHolder.singleton;
}
}
enum Utilisez enum. Cependant, les ressources peuvent être gaspillées.
enum
public enum Singleton7 {
SINGLETON;
}
L'essence n'est pas très différente de «statique final», mais elle est publique.
public static final Singleton7 SINGLETON = new Singleton7();
Méthode de mise en œuvre | Ressources gaspillées | Fil sûr | Prise en compte des performances des threads |
---|---|---|---|
Typique | NO | NO | - |
synchronized | NO | YES | NO |
Double Check Lock | NO | NO | - |
volatile + DCL | NO | YES | YES |
static fianl | YES | YES | YES |
Holder | NO | YES | YES |
enum | YES | YES | YES |
Dans l'ensemble, «volatile + DCL» ou «Holder» est le meilleur!
Recommended Posts