1、定义
• 确保一个类只有一个实例,且自行实例化,并向整个系统提供这个实例,这个类称为单例类,同时提供一个唯一的访问方法。
2、要点
• 一个类只有一个实例
• 必须自行创建实例
• 必须自行向整个系统提供这个实例
3、何时选用单例模式
• 系统只需要一个实例对象
• 客户调用类的单个实例,只允许使用一个公共访问点
• 单例模式可扩展为多例模式,即一个类可以有一个实例共存
4、本质
• 控制实例的数量
5、单例模式实现方式
• 饿汉式
package com.eager.test;
/**
* 饿汉式单例模式
*/
public class EagerSingleton {
//4:定义一个静态变量来存储创建好的类实例
//直接在这里创建类实例,只会创建一次
private static final EagerSingleton instance=new EagerSingleton();
//1:私有化构造方法,好在内部控制创建实例的数目
private EagerSingleton()
{
}
//2:定义一个方法来为客户端提供类实例
//3:这个方法需要定义成类方法,也就是要加static
//这个方法里面就不需要控制代码了
public static EagerSingleton getInstance()
{
return instance;
}
}
说明:
1)static变量,在类加载的时候进行初始化
2)多个static变量,共享一块内存区域
3)饿汉式的一个缺点就是不管这个实例是否被使用,该实例都会被创建,会有一点点浪费内存
• 普通懒汉式
package com.lazy.test;
/**
* 普通懒汉单例模式
*/
public class CommonLazySingleton {
private static CommonLazySingleton instance=null;
//私有的构造方法,以便在内部控制创建实例的个数
private CommonLazySingleton()
{
}
//synchronized
public static CommonLazySingleton getInstance()
{
//如果没有值,说明还没有创建过实例,那就创建一个
//并把这个实例设置给instance
if(instance==null)
{
instance=new CommonLazySingleton();
}
//如果有值则直接使用
return instance;
}
}
说明:普通懒汉式虽然利用延迟加载,解决了需要使用实例时才创建,但是这种方式是线程不安全的,假如A线程走到20行,但是还没有走到22行进行创建instance,此时,线程B也很快判断出instance==null,就会出现线程不安全的现象。
• 双重检查加锁懒汉式
package com.lazy.test;
/**
* 双重检查锁定懒汉单例模式
*/
public class DoubleCheckLockingLazySingleton {
/**
* 对保存实例的变量添加volatile的修饰
*/
private volatile static DoubleCheckLockingLazySingleton instance = null;
private DoubleCheckLockingLazySingleton()
{
}
public static DoubleCheckLockingLazySingleton getInstance()
{
//先检查实例是否存在,如果不存在才进入下面的同步块
if(instance == null)
{
//同步块,线程安全的创建实例
synchronized(DoubleCheckLockingLazySingleton.class)
{
//再次检查实例是否存在,如果不存在才真的创建实例
if(instance == null)
{
instance = new DoubleCheckLockingLazySingleton();
}
}
}
return instance;
}
}
说明:
1)双重检查加锁方式解决了延迟加载与线程安全问题,但是用到了Java中的关键字volatile,被volatile修饰的变量的值,不会被本地线程所缓存,所以对该变量的读取都是从共享内存中读取,从而保证多线程的安全性。
2)Java1.4及以前版本中,很多JVM对于volatile关键字的实现有问题,会导致双重检查加锁的失败,因此双重检查加锁的机制只能用再Java5及以上版本
3)由于volatile关键字可能会哦ing比掉JVM中一些必要的代码优化,因此运行效率不高。所以没有特别的需要,不建议使用。所以双重检查加锁策略虽然可以解决问题,但是不建议大量采用。
• IoDH懒汉式
package com.lazy.test;
/**
* IoDH懒汉单例模式
*/
public class IoDHLazySingleton {
private IoDHLazySingleton()
{
}
/**
* 静态内部类,该内部类的实例与外部类的实例没有绑定关系,而且只有被调用才会装载,实现延迟加载
*
* 多个实例的static变量会共享同一块内存区域
*/
private static class HolderClass
{
private final static IoDHLazySingleton instance=new IoDHLazySingleton();
}
/**
* 提供给其他类获取唯一实例的方法
*/
public static IoDHLazySingleton getInstance()
{
return HolderClass.instance;
}
}
说明:利用静态内部类既实现了延迟加载,又实现了线程安全,因此推荐的使用方法之一即为此种方式。
• 枚举实现的饿汉式
package com.enum_test;
/**
* 使用枚举来实现单例模式的示例
*/
public enum SingletonEnum {
/**
* 定义一个枚举的元素,它就代表了SingletonEnum的一个实例
*/
INSTANCE;
/**
* 示意方法,单例可以有自己的操作
*/
public String print() {
System.out.println("Enum测试实例调用方法");
return "Enum单例模式测试";
}
// public static Singleton getInstance() {
// return INSTANCE;
// }
}
说明:
1)Java的枚举型实质上是功能齐全的类,可以有自己的属性和方法
2)Java的枚举型的基本思想是通过public static final 域为每个枚举常量导出实例对象的
3)因此,用枚举实现单例模式会更加简洁、方便、并且提供了序列化的机制,由JVM保证线程安全。
转载于:https://www.cnblogs.com/guoxiaoyue/p/4073258.html