一个栗子:
public class Singleton { private static Singleton uniqueInstance; private Singleton() { } public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1.equals(singleton2)); } }
注意构造器是private的,然后uniqueInstance是静态的。静态变量记录该类的唯一实例。
package src.Singleton; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 单例模式确一个类只有一个实例,并提供一个全局访问点 * 单例模式可以延迟初始化,在药品使用这个对象的时候才去创建它 */ public class ChocolateBoiler { private boolean empty; private boolean boiled; private static ChocolateBoiler uniqueInstance; private ChocolateBoiler() { empty = true; boiled = false; } public static ChocolateBoiler getInstance() { if (uniqueInstance == null) { uniqueInstance = new ChocolateBoiler(); } return uniqueInstance; } private boolean isEmpty() {return empty;} private boolean isBoiled() {return boiled;} public void fill() { if (!isEmpty()) { empty = false; boiled = false; } } public void drain() { if (!isEmpty() && isBoiled()) { empty = true; } } public void boil() { if (!isEmpty() && !isBoiled()) { boiled = true; } } public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); Thread thread = new Thread(){ public void run() { ChocolateBoiler chocolateBoiler = ChocolateBoiler.getInstance(); chocolateBoiler.fill(); chocolateBoiler.boil(); chocolateBoiler.drain(); } }; for (int i = 0; i < 5; i++) { service.execute(thread); //2个线程同时执行 } service.shutdown(); } }
想一想,这会存在什么问题吗?
是不是任何情况下都只是唯一的实例?当我们遇到多线程的时候,事情可能会变得不一样。
public class Singleton { private static Singleton uniqueInstance; private Singleton() { } public static synchronized Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1.equals(singleton2)); } }
解决方法很简单,就是在getinstance方法前加上
synchronized
但是这样直接对方法加锁的性能很低。
一些改进的方案:
1)对性能要求不高,就私用原来的直接同步方法
2)不延迟初始化,而是在jvm加载类的时候就创建实例 => 急切初始化:
如果程序一定会创建实例
public class ThreadSafeSingleton { private static ThreadSafeSingleton uniqueInstance = new ThreadSafeSingleton(); private ThreadSafeSingleton() {} public static ThreadSafeSingleton getInstance() { return uniqueInstance; } }
3)双重加锁
性能大大提升
public class DoubleLockSingleton { private volatile static DoubleLockSingleton uniqueInstance; private DoubleLockSingleton() {} public static DoubleLockSingleton getInstance() { if (uniqueInstance == null) { synchronized (DoubleLockSingleton.class) { if (uniqueInstance == null) { uniqueInstance = new DoubleLockSingleton(); } } } return uniqueInstance; } }
这三种就是线程安全的单例模式的三种形式,各自有各自的适用情况。