一、 定义
单例模式(Singleton Pattern):Ensure a class has only one instance,and provide a global point ofaccess to it.
其主要作用是控制某个类型的实例数量有且只有一个,即本质是控制实例数目。
二、 实现方式
1. 饿汉式单例
特点:类加载时实例化,其优点是线程安全
public class Singleton{
//在类初始化时,已经自行实例化
private static final Singleton instance = new Singleton();
privateSingleton() {}//避免了类在外部被实例化
public static SingletongetInstance() {
return instance;
}
}
(注:通过Java反射机制是能够实例化构造方法为private的类,那基本上会使所有的Java单例实现失效。)
2. 懒汉式单例
特点:在第一次调用时实例化 ,其优点是延迟加载
此实现方式体现了延迟加载的思想(Lazy Load,开始不加载资源和数据,使用时才加载),同时也体现了缓存的思想。
public class Singleton{
private static Singletoninstance = null;
privateSingleton() {}
public static synchronized SingletongetInstance() {//保证线程安全,但会降低速度
if (instance == null) {
instance = newSingleton();
}
return instance;
}
}
优化:使用双重检查加锁方法改进,只在第一次创建实例时同步,以后不需要同步,从而加快运行速度。
public class Singleton2{
//volatile不会被本地线程缓存,对变量的读写直接操作共享内存
private volatile static Singleton2instance = null;
privateSingleton2() {}
public static Singleton2getInstance() {//双重检查加锁
if (instance == null) {//整个过程只需要一次同步
//同步块,线程安全的创建实例
synchronized(Singleton2.class) {
if (instance == null) {
instance= new Singleton2();
}
}
}
return instance;
}
}
3. 登记式单例
import java.util.HashMap;
import java.util.Map;
//类似Spring里面的方法,将类名注册,下次从里面直接获取。
public class Singleton3{
private staticMap<String,Singleton3> map = newHashMap<String,Singleton3>();
static{
Singleton3 single = new Singleton3();
map.put(single.getClass().getName(),single);
protected Singleton3(){}//保护的默认构造子
//静态工厂方法,返还此类惟一的实例
public static Singleton3getInstance(String name) {
if(name == null) {
name = Singleton3.class.getName();
System.out.println("name== null"+"--->name="+name);
}
if(map.get(name)== null) {
try {
map.put(name,(Singleton3) Class.forName(name).newInstance());
} catch(InstantiationException e) {
e.printStackTrace();
} catch(IllegalAccessException e) {
e.printStackTrace();
} catch(ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
public Stringabout() {
return "Hello,I am RegSingleton.";
}
public static voidmain(String[] args) {
Singleton single = Singleton.getInstance(null);
System.out.println(single3.about());
}
}
4. 即实现延迟加载,又保证线程安全,使用Lazy initialization holder class模式
此方法综合使用了Java的类级内部类和多线程默认的同步锁,巧妙地实现了延迟加载和线程安全。
public class Singleton4{
/**
* 类级的内部类,即静态的成员内部类,该内部类的实例与外部类的实例没有绑定关系,只有被调用时才装载,从而实现延迟加载
*/
private static classSingletonHolder {
/*
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton4instance = new Singleton4();
}
privateSingleton4() {}
public static Singleton4getInstance() {
returnSingletonHolder.instance;
}
}
5. 利用缓存来实现单例模式
利用缓存的基本知识,每次都先从缓存中取值,只要创建过一次对象实例后就设置了缓存的值,那么下次就不需要在创建了。(再次先不考虑多线程的问题)
public class Singleton5{
private final static String D_KEY = "one";
privateSingleton5() {}
// 缓存实例的容器
private staticMap<String, Singleton5> map = newHashMap<String, Singleton5>();
public static Singleton5getInstance() {
Singleton5 instance = map.get(D_KEY);
if (instance== null) {
instance = new Singleton5();
map.put(D_KEY,instance);
}
return instance;
}
}
6. 单例和枚举
单元素的枚举类型是实现Singleton的最佳方法,Java中枚举类型除了不能继承外,和普通类没区别。使用枚举实现单例控制,更简单、高效、安全。
public enum Singleton6{
uniqueInstance;//只定义一个枚举的元素,它代表Singleton的一个实例
public voidsingletonOperation(){
//功能处理
}
}
三、 应用
1.优点:在内存只有一个实例,减少内存消耗,减少系统的性能开销,避免多资源的多重占用。
2. 在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。
3.单例模式扩展,如有上限的多例模式。
4.一个应用实例:
public classConfigManager {
private static final String FIFLE = "d://data/user.properties";
private File file = null;
private long lastModifiedTime = 0;
private Propertiesprops = null;
private staticConfigManager instance = newConfigManager();
privateConfigManager() {
file = new File(FIFLE);
lastModifiedTime = file.lastModified();
if (lastModifiedTime == 0) {
System.out.println(FIFLE + "file does not exist!");
}
props = newProperties();
try {
props.load(newFileInputStream(FIFLE));
} catch(IOException e) {
e.printStackTrace();
}
}
/*
* 静态工厂方法
*/
public static synchronized ConfigManagergetInstance() {
return instance;
}
/*
* 获取属性配置项的值
*/
public final ObjectgetConfigItem(String name, Object defaultVal) {
long newTime = file.lastModified();
if (newTime== 0) {
// 属性文件不存在
if (lastModifiedTime == 0) {
System.out.println(FIFLE + "file does not exist!");
} else {
System.out.println(FIFLE + "file was deleted!");
}
return defaultVal;
} else if (newTime> lastModifiedTime) {
props.clear();
try {
props.load(newFileInputStream(FIFLE));
} catch(IOException e) {
e.printStackTrace();
}
}
lastModifiedTime = newTime;
Object val = props.getProperty(name);
if (val == null) {
return defaultVal;
} else {
return val;
}
}
}