单例模式
介绍
单例模式是Java中常用的一种设计模式。单例模式的定义是确保一个类只有一个实例,而且确保自行实例化并向整个系统提供这个实例
分类
单例模式的实现方式懒汉单例、饿汉单例、登记式单例
懒汉式单例
介绍
懒汉式单例,顾名思义懒汉式单例之所以被叫做懒汉式是因为懒汉式单例调用取得实例方法的时候才会实例化对象
实例
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
分析
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问,实例延迟加载。
- 缺点
懒汉单例模式是没有考虑线程安全,并发环境下多个Singleton实例,要实现线程安全,有以下三种方式,保证线程安全,
- 在getInstance方法上加同步
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 static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
- 静态内部类
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
这种方法比上述两种方法都好,既实现了线程安全,又避免了同步带来的性能影响
饿汉式单例
饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
登记式单例(用的比较少)
登记式单例维护的是一组实例对象,实例对象存储在map中,
//类似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());
}
}
懒汉和饿汉的区别
- 线程安全
懒汉是线程不安全的,为实现线程安全可以有上述的三种写法,这三种方法资源加载和性能各有不同
饿汉是天生线程安全的,直接作用与多线程环境不会出问题,所以一般情况下饿汉模式的单例比较常用
- 资源加载和性能
懒汉模式,顾名思义会延迟加载,第一次使用该单例才会实例化对象,所以会造成延迟加载。而且为了解决线程安全问题,懒汉模式会加同步锁,造成性能问题
饿汉模式在类创建的同时就实例化一个静态对象出来,虽然不管之后会不会使用这个单例,都会占据一个内存,但是第一次调用的时候速度就快很多
总结
优点
- 在内存中只有一个对象,节省内存空间。
- 避免频繁的创建销毁对象,可以提高性能。
- 避免对共享资源的多重占用。
- 可以全局访问。
适用场景
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象。