单例模式
1.单例模式概述
基本定义
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例
对于系统的某些类来说,只含有一个实例很重要,有助于我们协调系统的整体行为。比如线程池、缓存、日志对象
等等都会被设计为单例,设计的初衷也是为了避免不一致状态。
2.常见的单例模式
饿汉式单例
public class Singleton {
private Singleton(){}
private final static Singleton SINGLETON = new Singleton();
public static Singleton getInstance(){
return SINGLETON;
}
}
懒汉式单例
- 线程不安全,单线程环境下可以使用。
- 延迟加载对象。
- 多线程环境下可能会产生多个Singleton对象
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
懒汉式单例(同步方法解决线程安全问题)
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
懒汉式单例(同步实例-线程不安全写法)
- 原本觉得同步整个方法的效率会比较低,假设创建实例中还存在其他的一些操作。所以改成同步创建实例那一步。
- 在if(singleton == null)这一步仍然会产生线程安全问题。
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
懒汉式单例(双重检查----推荐使用)
public class Singleton {
/*volatile 关键字确保当uniqueInstance变量被初始化成Singleton实例时,
多个线程正确地处理uniqueInstance
*/
private static volatile Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
懒加载单例(静态内部类实现----推荐使用)
public class Singleton {
private static boolean initialized =false;
/*
要想使用内部类,必然需要先初始化内部类,
如果不使用,则不会加载内部类。
* */
//构造方法这一段是假如反射攻击的时候能抛出异常。
private Singleton(){
synchronized (Singleton.class){
if (initialized==false){
initialized=initialized;
}else{
throw new RuntimeException("单例已经被侵犯");
}
}
}
//当调用这个方法的时候将会加载内部类
//static 为了使得单例空间可以共享
//final 保证这个方法不会被重写重载
public static Singleton getInstance(){
//在访问这个结果以前,一定会先加载下面的内部类
return LazyHolder.LAZY_THREE;
}
//默认不加载
private static class LazyHolder{
private static final LazyThree LAZY_THREE =new Singleton();
}
}
懒加载单例(枚举方式----推荐使用)
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
3.spring中单例模式的实现:注册式
//Spring 中的单例实现 注册式实现
public class @R_301_50@ {
/*
注册式单例维护的实际是一组单例类,若已经注册过,那么从IOC容器中直接返回,
若没有注册过,先注册,再返回。
*/
public static Map<String,Object> ioc=new ConcurrentHashMap<>();
public static Object getBean(String className){
if(ioc.containsKey(className)){
return ioc.get(className);
}else{
Class clazz=null;
Object obj= null;
try {
obj = Class.forName(className).newInstance();
return ioc.put(className,obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
}
4.解决反序列化重新创建对象的问题
public class Seriable implements Serializable {
public final static Seriable INSTANCE =new Seriable();
private Seriable(){}
public static Seriable getInstance(){
return INSTANCE;
}
/*在反序列化的时候会重新创建对象 (new )
* 但是有的对象是单例的呢,你不能创建多个对象吧
* 所以通过下面这个方式可以控制只生成一个对象。
* */
private Object readResolve(){
return INSTANCE;
}
}
5.单例模式为什么必须是静态的
先搞清单例模式的实现过程:
- 先将该类的构造函数私有化(目的是禁止其他程序创建该类的对象);
- 其次,在本类中自定义一个对象(既然禁止其他程序创建该类的对象,就要自己创建一个供程序使用,否则类就没法用,更不是单例);
- 最后,提供一个可访问类自定义对象的类成员方法(对外提供该对象的访问方式);
核心就是你只能用我自己创建的对象。
程序调用类的方式:
上面说了,构造函数私有化后第一种情况就不能用,只能使用第二种方法。
而使用类名直接调用类中方法,类中方法必须是静态的,而静态方法不能访问非静态成员变量,因此类自定义的实例变量也必须是静态的。这就是单例模式为什么必须是静态的原因。
谢谢以下前辈:
参考:https://www.cnblogs.com/zhaoyan001/p/6365064.html
参考:https://zhidao.baidu.com/question/2206072272164938188.html