In diesem Artikel wird weiterhin das Singleton-Muster in der Reihe von 23 Designmustern vorgestellt.
Konzept:
Das Singleton-Muster ist ein gängiges Designmuster in Java. Es gibt drei Haupttypen, um das Singleton-Muster zu schreiben: „Lazy Singleton“ und „Hunger Man“. Singleton.
Der Singleton-Modus hat die folgenden Eigenschaften:
1. Eine Singleton-Klasse kann nur eine Instanz haben.
2. Die Singleton-Klasse muss ihre eigene eindeutige Instanz erstellen.
3. Die Singleton-Klasse muss diese Instanz allen anderen Objekten bereitstellen.
Das Singleton-Muster stellt sicher, dass eine Klasse nur eine Instanz hat, sich selbst instanziiert und diese Instanz dem gesamten System zur Verfügung stellt. In Computersystemen werden Thread-Pools, Caches, Protokollobjekte, Dialogfelder, Drucker und Grafikkartentreiberobjekte häufig als Singletons konzipiert. Diese Anwendungen verfügen alle mehr oder weniger über die Funktionalität von Ressourcenmanagern. Jeder Computer kann über mehrere Drucker verfügen, es kann jedoch nur ein Druckerspooler vorhanden sein, um zu verhindern, dass zwei Druckaufträge gleichzeitig auf dem Drucker ausgegeben werden. Jeder Computer kann über mehrere Kommunikationsanschlüsse verfügen. Das System sollte diese Kommunikationsanschlüsse zentral verwalten, um zu verhindern, dass ein Kommunikationsanschluss gleichzeitig von zwei Anforderungen aufgerufen wird. Kurz gesagt besteht der Zweck der Wahl des Singleton-Modus darin, inkonsistente Zustände und langfristige Richtlinien zu vermeiden.
1. Lazy Singleton
//懒汉式单例类.在第一次调用的时候实例化自己 public class Singleton { private Singleton() {} private static Singleton single=null; //静态工厂方法 public static Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; } }
Singleton verhindert, dass die Klasse extern instanziiert wird, indem die Konstruktionsmethode auf privat beschränkt wird Auf die Instanz von kann nur über die Methode getInstance() zugegriffen werden.
(Tatsächlich ist es durch den Java-Reflexionsmechanismus möglich, eine Klasse mit einem privaten Konstruktor zu instanziieren, wodurch grundsätzlich alle Java-Singleton-Implementierungen ungültig werden. Dieses Problem wird hier nicht besprochen, also verstecken wir es für Es wird davon ausgegangen, dass der Reflexionsmechanismus nicht vorhanden ist. Die obige Implementierung von Lazy Singleton berücksichtigt jedoch keine Thread-Sicherheit Um eine Thread-Sicherheit zu erreichen, gibt es die folgenden drei Methoden, die alle die getInstance-Methode ändern, um die Thread-Sicherheit von Singletons im Lazy-Stil sicherzustellen Wenn Sie viel über Thread-Sicherheit wissen, können Sie zunächst die folgenden drei Schritte überspringen, einen Blick auf den Singleton im Hungry-Stil werfen und dann nach dem Lesen noch einmal über Thread-Sicherheitsprobleme nachdenken:
1. Synchronisierung zur getInstance-Methode hinzufügen
public static synchronized Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; }
public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; }
public class Singleton { private static class LazyHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return LazyHolder.INSTANCE; } }
2. Hungriger Singleton im chinesischen Stil
Hungriger chinesischer Stil in When Eine Klasse wird erstellt, ein statisches Objekt wird für die Systemverwendung erstellt und wird in Zukunft nicht mehr geändert, sodass es von Natur aus Thread-sicher ist.
//饿汉式单例类.在类初始化时,已经自行实例化 public class Singleton1 { private Singleton1() {} private static final Singleton1 single = new Singleton1(); //静态工厂方法 public static Singleton1 getInstance() { return single; } }
3. Registrierter Singleton (kann ignoriert werden)
Registrierter Singleton wird tatsächlich verwaltet. Erstellen Sie einen Satz von Instanzen der Singleton-Klasse und speichern Sie diese Instanzen in einer Map (Registrierungsbuch). Für Instanzen, die registriert wurden, werden sie direkt von der Map zurückgegeben. Für diejenigen, die nicht registriert wurden, werden sie zuerst registriert und dann zurückgegeben.
Hier habe ich den Singleton im Registrierungsstil als vernachlässigbar markiert. Nach meinem Verständnis wird er aufgrund des statischen Methodenblocks immer noch weniger verwendet Darin wird sein Singleton instanziiert, wenn die Klasse geladen wird.
//类似Spring里面的方法,将类名注册,下次从里面直接获取。 public class Singleton3 { private static Map<String,Singleton3> map = new HashMap<String,Singleton3>(); static{ Singleton3 single = new Singleton3(); map.put(single.getClass().getName(), single); } //保护的默认构造子 protected Singleton3(){} //静态工厂方法,返还此类惟一的实例 public static Singleton3 getInstance(String name) { if(name == null) { name = Singleton3.class.getName(); System.out.println("name == null"+"--->name="+name); } if(map.get(name) == null) { try { map.put(name, (Singleton3) Class.forName(name).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return map.get(name); } //一个示意性的商业方法 public String about() { return "Hello, I am RegSingleton."; } public static void main(String[] args) { Singleton3 single3 = Singleton3.getInstance(null); System.out.println(single3.about()); } }
Der Unterschied zwischen hungrigem Mann und faulem Mann
Vom Namen her hungriger Mann und fauler Mann,
1. Thread-Sicherheit:
Hungriger chinesischer Stil ist geboren Es ist threadsicher und kann problemlos direkt für Multithreading verwendet werden
Der hungrige chinesische Stil instanziiert ein statisches Objekt, wenn die Klasse erstellt wird, unabhängig davon, was später passiert. Wenn Sie diesen Singleton nicht verwenden, belegt er eine gewisse Menge an Speicher, aber die Geschwindigkeit ist entsprechend höher, wenn Sie ihn zum ersten Mal aufrufen, da seine Ressourcen initialisiert wurden,
什么是线程安全?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。
应用
以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:
public class TestSingleton { String name = null; private TestSingleton() { } private static volatile TestSingleton instance = null; public static TestSingleton getInstance() { if (instance == null) { synchronized (TestSingleton.class) { if (instance == null) { instance = new TestSingleton(); } } } return instance; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void printInfo() { System.out.println("the name is " + name); } }
可以看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的作用,为什么还要加volatile呢,原因已经在下面评论中提到,
还有疑问可参考http://www.iteye.com/topic/652440
和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
public class TMain { public static void main(String[] args){ TestStream ts1 = TestSingleton.getInstance(); ts1.setName("jason"); TestStream ts2 = TestSingleton.getInstance(); ts2.setName("0539"); ts1.printInfo(); ts2.printInfo(); if(ts1 == ts2){ System.out.println("创建的是同一个实例"); }else{ System.out.println("创建的不是同一个实例"); } } }
运行结果:
结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。
对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。