单例模式
1.定义
-
所谓单例,就是整个程序有且仅有一个实例。该类负责创建自己的对象,同时要确保只有一个对象被创建。
-
好处:就是一个类你只能创建一个实例对象可以节约内存开销,一般常用于在工具类的实现(之前的静态方法也可以用于工具类的实现)。
2.特点
- 构造器私有
- 持有自己类的属性
- 对外提供获取实例的静态方法
一般单例模式分为两大类:懒汉模式,饿汉模式.详细分其实有懒汉,饿汉,双检锁,内部类,枚举。
3.懒汉模式
/**
* 懒汉模式,延迟初始化,在调用方法获取的时候才会实例对象
* 线程不安全的,严格意义上不是单例模式
* @author Administrator
*
*/
public class Singleton {
//持有自己类型的属性
private static Singleton instance;
//构造方法私有化
private Singleton() {}
//对外提供获取实例的静态方法
public static Singleton getInstance() {
if(instance==null) {
instance=new Singleton();
}
return instance;
}
}
测试
public class Test {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1==instance2);
}
}
结果
true
4.饿汉模式
线程安全,比较常用,但是容易产生垃圾,因为饿汉模式一开始加载类的时候就初始化了实例。
/**
* 饿汉模式
* @author Administrator
*
*/
public class Singleton {
//持有自己类型的属性
//由于static修饰,只在加载类的时候执行一次
private static Singleton instance = new Singleton();
//构造方法私有化
private Singleton() {}
//对外提供获取实例的静态方法
public static Singleton getInstance() {
return instance;
}
}
测试
public class Test {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1==instance2);
}
}
结果
true
懒汉模式和饿汉模式的区别
- 懒汉是非线程安全,饿汉是线程安全
- 懒汉模式是一开始不实例化对象,调用方法时才实例化对象,饿汉模式一开始实例化对象,容易造成资源浪费.
综合两者的优缺点,可以使用双检锁模式。
5.双检锁模式
双检锁也称双重校验锁,综合了懒汉模式和饿汉模式的优缺点
/**
* 双检锁模式结合懒汉模式和饿汉模式的优点:延迟加载并且线程安全
* @author Administrator
*
*/
public class Singleton {
//持有自己类型的属性
private volatile static Singleton instance;
//构造方法私有化
private Singleton() {}
//对外提供获取实例的静态方法
public static Singleton getInstance() {
if(instance==null) {
synchronized(Singleton.class) {
if(instance==null) {
instance=new Singleton();
}
}
}
return instance;
}
}
小结:
- 双检锁模式,进行两次判断,第一次判断是为了避免不要的实例,第二次是为了进行线程同步,避免多线程问题,由于newsingleton()创建instance对象的时候jvm中可能会重新排序,在多线程存在风险,使用volatile关键字可以当线程改变其值后通知其他线程已改变并且不会被java重新排序,解决该问题
6.内部类模式
/**
* 内部类模式实现单例模式
* @author Administrator
*
*/
public class Singleton {
private Singleton() {}
public static Singleton gatInstance() {
return Inner.instance;
}
public static class Inner{
//final修饰的常量,不能再被改变
private static final Singleton instance= new Singleton();
}
}
小结
- 只有第一次调用gatInstance()方法时,虚拟机才加载Inner内部类并初始化instance,只有一个线程可以获取instance对象的初始化,其他线程无法再进行初始化,线程安全的,保证对象的唯一性。【推荐使用此方式实现单例模式】
7.枚举实现
/**
* 默认枚举的实例是线程安全的,并且在任何情下都是单例
* 枚举类默认隐藏了所有的构造方法
*
* @author Administrator
*
*/
public enum Singleton {
INSTANCE;
public static Singleton getInstance() {
return Singleton.INSTANCE;
}
}