Java 单例模式
单例模式:
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
因为只产生一个实例,减少了系统实例开销,当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式解决
一.概念
1.需要生成唯一序列的环境
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
4.方便资源相互通信的环境
5.单例类只能有一个实例。
6.单例类必须自己自己创建自己的唯一实例
7.单例类必须给所有其他对象提供这一实例
8.单例类必须自己自己创建自己的唯一实例
9.单例类必须给所有其他对象提供这一实例
Java 单例模式有五种实现方式:
1.饿汉式
2.懒汉式
3.Double CheckLock实现单例
4.静态内部类模式
5.枚举类
二.饿汉式
特点:线程安全,调用效率高,但是不能延时加载
public class Demo1 {
实例化这个类
private static final Demo1 instance=new Demo1 ();
隐藏构造器(或者叫私有构造器)
Private Demo1(){
}
创建静态工厂方法,让外部可以获取实例
public static Demo1 getInstance (){
return instances;
}
}
俄汉式单例设计模式代码中,static变量会在类加载时初始化,此时不会设计多个线程对象访问该对象的问题,虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题,因此,可以省略synchronized关键字
三.懒汉式
特点:线程安全,调用效率不高,但是能延时加载
public class Demo2{
类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
private static Demo2 instance;
私有化构造器
private Demo2 () {
}
方法同步,调用效率低
创建静态工厂方法 ,让外部可以获取实例
public static synchronized Demo2 getInstance() {
if (instance == null) {
instance = new Demo2 ();
} return instance;
}
}
四.Double CheckLock实现单例:
Double CheckLock也就是双重锁判断机制(由于JVM底层模型原因,偶尔会出问题, 不建议使用),是在懒汉式单例上发展而来
public class Demo3{
private volatile static Demo3 instance;
私有化构造器
private Demo3 () {
}
静态工厂方法,双重锁判断机制 public static Demo3 newInstance() {
if (instance == null) {
synchronized (Demo3.class) {
if (instance == null) {
instance = new Demo3 ();
}
}
}
return instance;
}
}
如果将同步内容下放到if内部,提高了执行i效率,不必每次获取对象时进行同步,只有第一次才同步创建以后就没有必要了
由于编译器优化和JVM底层内部模型原因,偶尔会出问题,不建议使用
五.静态内部类模式
特点:线程安全,调用效率高,可以延时加载
public class Demo4 {
静态内部类
public static class Demo4ClassInstance
{
private static final Demo4 instance = new Demo4();
}
私有化构造器
private Demo4()
{
}
静态工厂方法
public static Demo4 getInstance()
{
return Demo4 ClassInstance.instance;
}
}
外部类没有static属性,则不会像俄汉式那样立即加载对象
只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。instance是static final 类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证线程安全性
六.枚举类:
特点:线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用
public class Demo5{
私有化构造器
private Demo5 (){
}
使用枚举
private static enum Singleton{
instance;
private Demo5 singleton;
JVM会保证此方法绝对只调用一次
private Singleton(){
singleton = new Demo5 ();
}
public Demo5 getInstance(){
return singleton;
}
}
静态工厂方法
public static Demo5getInstance()
{
return Singleton.instance.getInstance();
}
}
实现简单,枚举本身就是单例模式。由于JVM提供保障避免通过反射和反序列化的漏洞
无延时加载
输出:
结果:
如何选择使用:
单例对象占用资源少,不需要延时加载时:枚举 好于 饿汉 单例对象占用资源多,需要延时加载时:静态内部类 好于 懒汉式