我们在写程序的时候,大部分时候,会把类定义成为public类型的,那么任何类都可以随意的创建该类的对象。但是有时候,这种做法并没有任何意义,频繁的创建对象和回收对象造成内存损耗,所以就有了单例模式
一个类只能创建一个对象,则这个类被成为单例类,这种模式被成为单例模式
单例模式的原则是:
1.把类的构造方法隐藏起来
2.创建一个方法,这个方法可以创建一个(并且只有一个)自己的实例
3.这个方法可以被外部使用
单例模式分为:懒汉模式、饿汉模式和等级模式
懒汉模式:
/** * 懒汉模式,特征是待到使用时,才创建该类对象 */
public class Single {
// 定义一个类变量,来存储该类对象 private static Single single; // 隐藏构造器 private Single() { } // 定义一个方法供外部调用,返回一个该类唯一的对象 public static Single getSingle() { if (single == null) { single = new Single(); } return single; } public static void main(String[] args) { Single s1 = Single.getSingle(); Single s2 = Single.getSingle(); if (s1 == s2) { System.out.println("Single类是单例模式"); } else { System.out.println("Single类不是单例模式"); } } }
饿汉模式:
/** * 饿汉模式 饿汉模式特征是在类加载时候已经创建好实例待调用,并且该实例不可变 */
public class Single { // 直接创建该类的实例,该实例不可变 private static final Single single = new Single(); // 隐藏构造函数 private Single() { } // 创建一个方法供外部调用,返回该类的实例 public static Single getSingle() { return single; } public static void main(String[] args) { Single s1 = Single.getSingle(); Single s2 = Single.getSingle(); if (s1 == s2) { System.out.println("Single类是单例模式"); } else { System.out.println("Single类不是单例模式"); } } }
登记模式:
import java.util.HashMap; import java.util.Map; import java.util.Set; /** * 单例模式之登记模式,特征是事先维护一组map存放一个实例,当创建实例时,先查看map是否存在,如果存在,则直接返回,如果不存在,则先存入,再返回 */ public class Single { // 定义一个map,用来登记实例 private static Map<String, Single> map = new HashMap<String, Single>(); // 先在map中存放一个Single实例 static { Single single = new Single(); map.put(single.getClass().getName(), single); } // 隐藏构造器 private Single() { } // 定义一个方法供外部调用,用来返回实例,需要传入一个参数作为map中的“键” public static Single getSingle(String key) { // 如果传入的参数为null,则讲类名称赋值给它 if (key == null) { key = Single.class.getName(); } // 根据传入的“键”判断是否存在“值”,如果不存在,则将该 if (map.get(key) == null) { try { map.put(key, (Single) Class.forName(key).newInstance()); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return map.get(key); } public static void main(String[] args) { Single single1 = Single.getSingle(null); Single single2 = Single.getSingle(null); System.out.println(single1 == single2); } }
单例中的线程安全问题
饿汉式这种一开始就创建实例的方式,线程是安全的,懒汉式则不同,看代码
public class Single { // 定义一个类变量,来存储该类对象 private static Single single; // 隐藏构造器 private Single() { } // 定义一个方法供外部调用,返回一个该类唯一的对象 public static Single getSingle() { if (single == null) { //当线程执行到这里挂起时,别的线程获取执行权限执行到这里,还会继续给single创建实例,这样就造成了线程安全问题。 single = new Single(); } return single; } public static void main(String[] args) { Single s1 = Single.getSingle(); Single s2 = Single.getSingle(); if (s1 == s2) { System.out.println("Single类是单例模式"); } else { System.out.println("Single类不是单例模式"); } } }
为了避免懒汉式的线程安全问题,我们需要给他加上代码同步
public class ThreadForSingle { public static void main(String[] args) { } } class Single { private Single() { } private static Single single = null; public static Single getSingle() { synchronized (Single.class) { if (single == null) { single = new Single(); } } return single; } }
当线程进来判断出single为null时,遇到问题挂起,其余的线程则无法进来,这样就保证了线程的安全