Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点。
核心知识点如下:
(1) 将采用单例设计模式的类的构造方法私有化(采用private修饰)。
(2) 在其内部产生该类的实例化对象,并将其封装成private static类型。
(3) 定义一个静态方法返回该类的实例。
一:饿汉模式
优点是:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题;
缺点是:当类SingletonTest被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。
package cn.design.singleton; /** * 饿汉模式 * 优点是:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题; * 缺点是:当类SingletonTest被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间 * 从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例),当类被卸载时,静态变量被摧毁,并释放所占有的内存,因此在某些特定条件下会耗费内存。 * @author 翎野君 * */ public class SingletonTest { //将构造方法私有化,使得外部不可以访问 //只可以从定义的getInstance方法获取实例化对象 //防止外部通过new SingleTonTest()实例化对象 private SingletonTest(){ } /** * instance外部不可以直接访问,随着类加载而加载 * static静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配 * final成员变量表示常量,只能被赋值一次,赋值后值不再改变。 */ private static final SingletonTest instance=new SingletonTest(); //静态方法,不随对象的不同而改变 //返回上面定义的instance对象 public static SingletonTest getInstance(){ return instance; } public static void main(String[] args) { //在当前类下是可以访问的但是其他类不可以访问private变量 SingletonTest st=new SingletonTest(); //其他类中这样获取 SingletonTest st1=SingletonTest.getInstance(); } }
二:饱汉模式
优点是:写起来比较简单,当类SingletonTest被加载的时候,静态变量static的instance未被创建并分配内存空间,当getInstance方法第一次被调用时,初始化instance变量,并分配内存,因此在某些特定条件下会节约了内存;
缺点是:并发环境下很可能出现多个SingletonTest实例。
package cn.design.singleton; public class SingletonTest1 { //将构造方法私有化防止外部通过new SingleTest1()获取对象 private SingletonTest1(){ } //饱汉模式就是吃饱了,不着急等初始化对象的时候在获取一个唯一实例 //没有加final关键字,如果加上的话当即就要赋值 //而饱汉模式要求动态调用的时候创建唯一实例 private static SingletonTest1 instance; //定义一个静态方法等待调用的时候在对其进行对象初始化(多线程时无法保证只有一个对象) public static SingletonTest1 getInstance(){ if(instance==null){ instance=new SingletonTest1(); } return instance; } public static void main(String[] args) { SingletonTest1 st=SingletonTest1.getInstance(); System.out.println(st); } }
三:饱汉模式的优化
优点是:使用synchronized关键字避免多线程访问时,出现多个SingletonTest实例。
缺点是:同步方法频繁调用时,效率略低。
package cn.design.singleton; /** * 优化饱汉模式 * 优点:加锁防止多线程访问时出现多个实例的问题 * 缺点:同步方法频繁调用时,效率略低。 * @author 翎野君 * */ public class SingletonTest2 { private SingletonTest2(){ } private static SingletonTest2 instance; //定义一个静态方法,调用时进行初始化 //加上一把锁synchronized之后防止出现多线程并发调用时出现多个实例的问题 public synchronized static SingletonTest2 getInstance(){ if(instance==null){ instance=new SingletonTest2(); } return instance; } }
四:最优方案(不考虑反射的情况)
方法四为单例模式的最佳实现。内存占用地,效率高,线程安全,多线程操作原子性。
package cn.design.singleton; public class SingletonTest3 { private SingletonTest3(){ } //使用volatile保证了多线程访问时instance变量的可见性, //避免了instance初始化时其他变量属性还没赋值完时,被另外线程调用 //使用volatile保证了多线程访问时instance变量的可见性,避免了instance初始化时其他变量属性还没赋值完时,被另外线程调用 private static volatile SingletonTest3 instance; public static SingletonTest3 getInstance(){ if(instance==null){ 同步代码块(对象未初始化时,使用同步代码块,保证多线程访问时对象在第一次创建后,不再重复被创建) synchronized (SingletonTest3.class) { System.out.println(SingletonTest3.class); instance=new SingletonTest3(); } } return instance; } public static void main(String[] args) { SingletonTest3 st=SingletonTest3.getInstance(); } }