概述
这种模式涉及到一个单一的类,只能有一个实例,由自己创建。这个类向外提供访问其唯一的对象的方法,可以直接访问,不能也不需要实例化该类的对象。
意图 : 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
**主要解决:**一个全局使用的类频繁地创建与销毁。
**何时使用:**当您想控制实例数目,节省系统资源的时候。
**如何解决:**判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
**关键代码:**构造函数是私有的。
使用场
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
- 1、一个班级只有一个班主任。
简单实例(饿汉式)
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
/*
if (instance == null) {
instance = new Singleton();
}
如果在这里初始化,那么只有第一次被调用才初始化,起到lazyload效果, 即 非线程安全懒汉式
*/
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessae();
}
}
几种实现方式
1懒汉式,线程不安全(简单实例 即是)
2懒汉式,线程安全
lazy loading,能够在多线程中很好的工作
须加锁 synchronized 才能保证单例,效率极低,很大程度不能同步
适用于 调用getinstance不频繁
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3饿汉式(上文简单实例)
不lazyload,类加载就初始化,浪费空间 (实例话instance 耗费大时必须延迟加载)
通过classloader机制保证线程安全,不加锁效率高,
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4双重校验锁 DCL double-checked locking
结合懒汉,饿汉优点 lazyloading 线程安全高效 实例域
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
/*
并不是一个院原子操作,
1,分配内存空间
2,执行构造
3,对象引用指向这个空间
指令可能重排,如果线程a 132 执行3时进来另一个线程,执行到了第一个判断singleton == null
的语句(在sycronize外面,为true),,但是还没构造
*/
}
}
}
return singleton;
}
}
会有什么问题,反射可以打破private的构造,生成新的实例;
Constructor<Singleton> singletonConstructor = Singleton.class.getDeclaredConstructor();
singletonConstructor.setAccessible(true);
Singleton sing1=singletonConstructor.newInstance();
System.out.println(sing1);
怎么办,在构造函数内加入 syncronize( if(实例!=null) throw exption “禁止反射”);(当然,这种进使用你已经正经产生了一个实例,如果一开始就去反射,还是没办法禁止)
if(实例!=null)无法解决,那么用一个标志呢,比如一个 private boolen ,初始为false,构造时为false执行构造,之后改为ture,避免二次构造,但是如果通过反编译得到的你这个变量,任然能通过反射,setfiled更改,套娃…
反编译命令 javap -p xx.calss 还有个叫jad的反编译软件
5登记式/静态内部类
classloader 机制来保证初始化 instance 时只有一个线程
与DCL区别,dcl实例域,这个是静态域
与饿汉式区别,饿汉不延迟加载,这种延迟加载,Singleton 类被装载了,instance 不一定被初始化,只有显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6枚举
不lazy, 线程安全,自动支持序列化机制,绝对防止多次实例化。 (不支持反射)
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
枚举也是饿汉模式
总结
一般用 饿汉
要延迟加载 用DCL、登记
要反序列化创建对象,要安全。用 枚举