2021-11-10

设计模式–单列模式

知识点

总共8种方式
1)饿汉式(静态常量)

非常勤快,在对象还没有使用的时候就先创建出来了

1)构造器私有化(防止new)
2)类的内部创建对象
3)向外暴露一个静态的公共方法getlnstance()
4)代码实现

public class Singleton1(){
	private Singleton1(){
	}
	private final static Singleton1 instance = new Singleton1();
	public static Singleton1 getInstance(){
	return instance;
	}
}

优缺点:
1)优点:这种写法比较简单,就是在类装载的时候就完成实例化,避免了线程同步的问题。
2)缺点:在类装载的时候就完成实例化,没有达到LazyLoading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
3)这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazyloading的效果
4)结论:这种单例模式可用,可能造成内存浪费。

2)饿汉式(静态代码块)

优缺点跟上边一样的,可以说是等效于上边的普通饿汉式
package com.melo.design.单例模式.饿汉式_静态代码块

public class Singleton2{
	private Singleton2(){
	}
	private static Singleton2 instance;
	static {
		instance = new Singleton2;
	}
	public static Singleton2 getInstance(){
		return instance;
	}
}

3)懒汉式

等到用到了,再把对象创建出来
/**
*懒汉式
*线程安全
*/
public class Singleton{
	//私有构造方法
	private Singleton(){}
	//在成员位置创建该类的对象
	private static Singleton instance;
	//对外提供静态方法获取该对象
	public static synchronized Singleton getInstance(){
		if(instance == null){
			instance = new Singleton;
		}
		return instance;
	}
}

4)懒汉式(同步代码块)–不能使用

实际上做不到线程同步,因为在if的时候就会出现线程混乱问题
比如ifA进来,B再进来,A先使用class,然后new了,B后续没有再次进行判断,还是会再次去new
class Singleton{
	private static Singleton singleton;
	private Singleton(){}
	public static Singleton getInstance(){
		if(singleton == null){
			synchronized(Singleton.class){
				singleton = new Singleton;
			}
		}
		return singleton;
	}
}

5)懒汉式双检索(双重检查)

刚好解决了上边懒汉式同步代码快的问题,再多一步if判断

注意:双检索是说双重检查,而不是双重锁!!!!

package com.melo.design.单例莫模式.双检索;

public class Singleton{
	//注意volatile修饰
	private volatile static Singleton singleton;
	private Singleton(){}
	public static Singleton getSingleton(){
		if(singleton == null){
			synchronized(Singleton.class){
				if(singleton == null){
					singleton = new Singleton;
				}
			}
		}
		return singleton;
	}
}

注意:volatile
在多线程的情况下,可能会出现空指针的问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令的重排序操作。
要解决双重检查锁模式带来的空指针异常的问题,只需要使用volatile关键字,volatile关键字可以保证可见性和有序性。

  • 那这里那里可能会出现指令重排序呢,既然涉及到多条指令会重排序,那么说明这里的**new Singleton()**操作并不是原子性的
 - 先给singleton分配内存空间
 - 调用构造函数来初始化成员变量singleton
 - 让singleton引用指向所分配好的内存空间(此时instance就!=null了)
  • 那什么情况下指令重排序后会出现问题呢?
 -比如说**c - a - b这种情况下,先走c,然后此时别的贤臣调用了getSingleton,注意此时他判断singleton
 不等于null后,就会直接人return instance**了,然而得到的singleton却是不可用的,因为该对象还没有
 初始化,状态还处于不可用的状态,这样会导致异常的发生。
  • 所以我们就得加volatile关键字来修饰该变流量,便可避免指令重排序
    6)静态内部类
里面定义一个final静态常量
使用时直接返回静态内部类的final静态常量就好了!
public class Singleton{
	//静态内部类,里定义一个final静态常量
	private static class SingletonHolder{
		private static final Singleton INSTANCE = new Singleton();
	}
	private Singleton(){}
	public static final Singleton getInstance(){
		return SingletonHolder.INSTANCE;
	}
}

1)这种方式采用来了类装载的机制来保证初始化实例时只有一个线程
2)静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SinglletonInstance类,从而完成Singleton的实例化。
3)类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
4)优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
5)结论:推荐使用

7)枚举(防反射和反序化)

  • 可以继承也可以实现接口(似乎跟普通的class没有什么区别)
  • 要用直接StuUserService.INSTANCE.方法即可
 public enum StuUserService implements StuUserServiceInter{
	/**
	*该类的唯一实例
	*/
	INSTANCE;
	@Override
	public void test1(){
		System.out.println("111");
	}
	public static void main(String[] args){
		StuUserService.INSTANCE.test1();
	}
}

扩展知识
java枚举
基本用法

枚举的用法比较多,本文主要旨在介绍利用枚举实现单例模式的原理,所以这里也主要介绍一些相关的基础内容。首先,枚举类似类,一个枚举可以拥有成员变量,成员方法,构造方法,先来看看枚举最基本的用法:

enum Type{
	A,B,C,D;
}

创建enum时,编译器会自动为我们生成一个继承java.lang.Enum;的类,我们上面的enum可以简单看作:

class Type extends Enum{
	public static final Type A;
	public static final Type B;
	...
}

对于上面的例子,我们可以把Type看作一个类,而把A,B,C,D看作类的Type的实例。当然,这个构建实例的过程不是我们做的,一个enum的构造方法限制是private的,也就是不允许我们调用.
首先,枚举类似类,一个枚举可以拥有成员变量,成员方法,构造方法。
每一个枚举量看作是这个类的对象
同时可以设置成员变量,成员方法
然后可以根据成员变量设置相应的构造方法

反射和序列化破坏单例

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值