一、简介
单例模式的核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
常见案例:回收站
优点:
1、单例模式只生成一个实例,降低系统性能开销。
2、方便资源共享
常见单例模式:
饿汉式:线程安全、效率高、不能延迟加载,当全程没使用到该实例时,浪费系统资源。
懒汉式:线程安全(加锁)、调用效率相对饿汉式低,可以延迟加载。:
双重检测锁式:由于jvm内部底层模型原因,偶尔会出现问题,不建议使用
静态内部类式:线程安全,调用效率高,可以延迟加载
枚举单例:线程安全,调用效率高,不能延迟加载
二、简单实现
1、饿汉式单例模式简单实现
核心:
1、利用static关键字声进行类的实例化。
2、私有化构造器
3、提供一个public方法获取类的实例
代码如下:
package com.zwh.gof23;
/**
* 单例模式(饿汉式)
* 1、构造器私有化
* 2、类初始化时立即加载。
* 特点:
* 1、线程安全
* 2、调用效率高
* 3、不能延时加载
* @author zwh
*
*/
public class SingletonPatternStarving {
//类初始化时就加载这个对象(static关键字),此时不涉及多个线程对象访问该对象的问题,
//虚拟机保证只会加载一次该类,肯定不会发生并发访问的问题。
//但是如果只是加载本类,而不是要调用geiInstance(),甚至永远没有使用,则造成资源浪费
private static SingletonPatternStarving instanse=new SingletonPatternStarving();
//构造器私有化
private SingletonPatternStarving(){
}
public static SingletonPatternStarving getInstance(){
return instanse;
}
}
2、懒汉式单例模式简单实现
核心步骤与饿汉式一致,只是在声明实例变量时不进行初始化,在调用全局方法时进行初始化,代码如下:
package com.zwh.gof23;
/**
* 懒汉式到单例模式
* @author zwh
* 特点:
* 1、延迟加载,在调用到getInstance方法时才加载。资源利用率高
* 2、调用getInstance方法时需要同步,并发效率较低
*/
public class SingletonPatternSlacker {
//实例不初始化,需要时再进行初始化
private static SingletonPatternSlacker instance;
//私有化构造器
private SingletonPatternSlacker(){}
/**
* 初始化类的实例,并保证在调用本方法时才会创建实例,保证线程安全。
* @return
*/
public static synchronized SingletonPatternSlacker getInstance(){
if(null == instance){
instance = new SingletonPatternSlacker();
}
return instance;
}
}
3、双重检测锁单例模式
由于jvm内部底层模型原因,偶尔会出现问题,不建议使用。核心在于将锁放到if条件中。只有第一次初始化实例时需要进行同步。代码如下:
package com.zwh.gof23;
/**
* 双重检测锁单例模式 将同步内容放到if条件内,只有第一次创建实例时需要进行同步,提高效率
*
* @author zwh
*
*/
public class SingletonPatternDoubleCheckLock {
// 声明变量
private static SingletonPatternDoubleCheckLock instance;
/**
* 全局访问方法,获取类的实例
*
* @return
*/
public static SingletonPatternDoubleCheckLock getInstance() {
if (null == instance) {
SingletonPatternDoubleCheckLock sc;
synchronized (SingletonPatternDoubleCheckLock.class) {
sc=instance;
if(null==sc){
synchronized (SingletonPatternDoubleCheckLock.class) {
if(null==sc){
sc=new SingletonPatternDoubleCheckLock();
}
}
instance=sc;
}
}
}
return instance ;
}
// 私有化构造器
private SingletonPatternDoubleCheckLock() {
}
}
4、静态内部类实现单例模式
特点:线程安全,兼备并发高效调用和延迟加载的优势。
代码:
package com.zwh.gof23;
/**
* 静态内部类实现单利模式
* 1、只有调用全局访问方法时,才会初始化单例对象。可以延迟加载
* 2、加载类时线程安全
* 3、同时具备懒汉式和饿汉式的优势
*
* @author zwh
*
*/
public class SingletonPatternInnerStaticClass {
/**
* 静态内部类中声明并初始化外部类的实例
*
* @author zwh
*
*/
private static class SingletonClassInstance {
private static final SingletonPatternInnerStaticClass instance =
new SingletonPatternInnerStaticClass();
}
/**
* 全局获取单例的方法
*
* @return
*/
public static SingletonPatternInnerStaticClass getInstance() {
return SingletonClassInstance.instance;
}
/**
* 构造方法私有化
*/
private SingletonPatternInnerStaticClass() {
}
}
5、使用枚举实现单例模式
特点:实现简单,枚举本身就是单例模式,无延迟加载
实现代码:
package com.zwh.gof23;
/**
* 使用枚举实现单例模式
* @author zwh
* 1、调用效率高
* 2、避免反射漏洞
* 3、没有延迟加载
*/
public enum SingletonPatternEnum {
/**
* 定义一个元素,代表一个实例
*/
INSTANCE;
/**
* 对枚举元素的操作
*/
public void singletonOperation(){
}
}
三、测试
对上面的单利模式的实现进行测试,通过对比分别初始化的两个对象是否为同一个对象。代码如下:
package com.zwh.gof23.singleton;
/**
* 单例模式测试类
* @author zwh
*
*/
public class TestClient {
/**
* @param args
*/
public static void main(String[] args) {
testSingletonPatternStaving();
testSingletonPatternSlaker();
}
/**
* 测试饿汉式单利模式
*/
private static void testSingletonPatternStaving(){
SingletonPatternStarving s1=SingletonPatternStarving.getInstance();
SingletonPatternStarving s2=SingletonPatternStarving.getInstance();
System.out.println(s1==s2);
}
/**
* 测试懒汉式单例模式
*/
private static void testSingletonPatternSlaker(){
SingletonPatternSlacker s1=SingletonPatternSlacker.getInstance();
SingletonPatternSlacker s2=SingletonPatternSlacker.getInstance();
System.out.println(s1==s2);
}
}
输出均为true。
四、小结
实现单例模式当然肯定不仅仅是这5种方法,最重要的是要将设计模式运用在日常工作中。学以致用才是学习的目的。
关于选用哪一种方式,不需要延迟加载时,枚举式>饿汉式
需要延迟加载时:静态内部类方式>懒汉式。