概述
一、定义
单例模式,一个类保证全局唯一性,只能创建该类型一个对象,并提供一个全局访问点。
缺点:
1)没有接口,扩展困难,违反开闭原则;
2)如果扩展单例对象,需要修改代码。
二、类图
三、四种实现
1.饿汉式
1)直接new出来
public class HungrySingleton {
//直接new出来,全局唯一性
private static HungrySingleton instance = new HungrySingleton();
//防止被new出来,但可以通过反射创建
private HungrySingleton(){}
//全局唯一访问入口
public static HungrySingleton getInstance(){
return instance;
}
}
2)通过static块,在类加载的时候初始化实例
public class HungryStaticSingleton {
private static final HungryStaticSingleton instance;
//通过static块,在类加载的时候初始化实例
static {
instance = new HungryStaticSingleton();
}
private HungryStaticSingleton(){}
public static HungryStaticSingleton getInstance(){
return instance;
}
}
2.懒汉式
1)线程不安全
public class LazySimpleSingleton {
private static LazySimpleSingleton instance;
private LazySimpleSingleton(){}
//线程不安全
public static LazySimpleSingleton getInstance(){
if(instance == null) {
instance = new LazySimpleSingleton();
}
return instance;
}
}
2)使用锁使得线程安全,但效率低
public class LazySimpleSingleton {
private static LazySimpleSingleton instance;
private LazySimpleSingleton(){}
//线程不安全
public static synchronized LazySimpleSingleton getInstance(){
if(instance == null) {
instance = new LazySimpleSingleton();
}
return instance;
}
}
3)双重检查锁单例
特点:线程安全,效率提升,缺点可读性差,不够优雅。
/**
* 双重检查锁单例
* 优点:性能提高,线程安全
* 缺点: 可读性难度加大,不够优雅
*/
public class LazyDoubleCheckSingleton {
//volatile防止指令重排序
private static volatile LazyDoubleCheckSingleton instance;
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance(){
if(instance == null) {
//synchronized加锁,解决可见性问题
synchronized(LazyDoubleCheckSingleton.class) {
if(instance == null) {
//指令会重排(三步:创建对象引用;申请内存空间;对象引用指向该内存空间;)
instance = new LazyDoubleCheckSingleton();
}
}
}
return instance;
}
4)静态内部类
特点:提升了效率和解决了线程安全问题,同时实现方式优雅。
/**
* 静态内部类实现单例:
* 优点:写法优雅,利用了Java本身的语法特点,性能高,避免内存浪费。
* 缺点:反射可以破坏。
*/
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton(){}
public LazyStaticInnerClassSingleton getInstance(){
return InnerClass.INSTANCE;
}
private static class InnerClass{
private static LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
3.注册式
1)Enum注册
public enum EnumSingleton {
INSTANCE;
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
特点:通过Enum本身的java语法特性,底层有Map注册声明的对象,在类加载的时候对声明对象进行实例化,注册Map容器中。在实例化的过程中不能通过反射实例化Enum类,可以在Constructor类中找到对应代码。
public T newInstance(Object ... initargs)
...
//不能为Modifier.ENUM类,否则直接抛出throw new IllegalArgumentException("Cannot reflectively create enum objects")异常。
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
}
2)IoC容器注册
public class ContainerSingleton {
private ContainerSingleton(){}
private static volatile Map<String, Object> ioc = new ConcurrentHashMap<>();
public static Object getInstance(String className){
if(!ioc.containsKey(className)) {
try {
Object o = Class.forName(className).newInstance();
ioc.put(className, o);
}catch(Exception e) {
}
}
return ioc.get(className);
}
//双重检查锁实现
public static Object getInstance2(String className){
if(!ioc.containsKey(className)) {
synchronized (ContainerSingleton.class) {
if(!ioc.containsKey(className)) {
try {
Object o = Class.forName(className).newInstance();
ioc.put(className, o);
} catch (Exception e) {
}
}
}
}
return ioc.get(className);
}
}
特点:容器式单例,是将已经创建的单例对象放入一个ConcurrentHashMap容器中,去获取单例对象的时候判断容器中是否存在,思想还是懒汉式思想,只不过加了容器概念,有容器概率后,就适合创建不同的单例对象的场景,但是思想是懒汉式的话,也会有线程安全问题,需要加锁解决(例如用双重检查锁实现)!
4.ThreadLocal单例
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocalSingletonInstance =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){}
public static ThreadLocalSingleton getInstance(){
return threadLocalSingletonInstance.get();
}
}
四、常见例子
ServletContext、ServletConfig、ApplicationContext、DBPool等实现
五、反射、序列化破坏单例
(1)反射除了Enum不能破坏之外,其它都能被反射破坏单例。
(2)序列化和反序列化过程中需要序列化反序列化对象类内写readResolve()方法对实例化单例进行返回,那么反序列化返回和内存中是同一个对象,其实就是创建了两份。
public class SeriableSingleton implements Serializable {
private final static SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton(){}
public static SeriableSingleton getInstance() {
return INSTANCE;
}
public Object readResolve(){
return INSTANCE;
}
}
最后
以上就是平常手机为你收集整理的创建型模式--单例模式的全部内容,希望文章能够帮你解决创建型模式--单例模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复