单例模式(singleton)

单例模式(Singleton):

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

实现原理:

将构造方法改为私有,并对外提供一个可获得实例的公共静态方法。

应用场景:

  1. 类只需要一个实例,比如工厂模式的工厂。
  2. WEB中,点击量、在线人数等计数器。
  3. 创建一个对象需要消耗的资源过多,如连接数据库。

实例:

实现单例模式的方法总共有八种,下边先展示一个最简单的,解释完单例模式之后再展示其他。

饿汉式

饿汉第一式——静态变量

package 单例模式._1_饿汉式_静态变量;

// 测试类,测试调用
public class SingletonTest {
	public static void main(String[] args) {
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance==instance2);
	}

}

// 饿汉式(静态变量)
class Singleton{

	// 1. 构造函数私有化,其他类不能new
	private Singleton() {}
	// 2. 本类内部创建对象实例
	private final static Singleton instance = new Singleton();
	// 3. 提供一个公有的静态方法,返回实例对象
	public static Singleton getInstance() {
		return instance;
	}
}
输出结果:
true
分析:
  1. 首先从输出结果就可以看出开,调用两次得到的对象是同一个。
  2. 用private修饰的构造方法,外部类不能访问,也就不能new。只能通过单例类提供的方法获得。
  3. 单例类将自己作为一个属性,并使用static修饰,在类被加载时就会实例化一次本类,并且不会再次实例化。确保单例类只有一个实例对象。
优点:

简单

缺点:
  1. 因为是被static修饰的,所以在类加载时就直接完成实例化,会一直占用内存资源。
  2. 不能防止,反射后,可以new。
小结:

这种方式可以采用,但是可能造成内存浪费。

饿汉第二式——静态代码块

package 单例模式._2_饿汉式_静态代码块;

// 测试类,测试调用
public class SingletonTest {
	public static void main(String[] args) {
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance==instance2);
	}

}

// 饿汉式(静态变量)
class Singleton{

	// 1. 构造函数私有化,其他类不能new
	private Singleton() {}
	// 2. 本类内部创建对象实例
	private final static Singleton instance;
	// 3. 静态代码块创建单例对象
	static {
		instance = new Singleton();
	}
	// 4. 提供一个公有的静态方法,返回单例对象
	public static Singleton getInstance() {
		return instance;
	}
}
输出结果:
true
分析:

仅仅是吧实例化放在了static代码块了。实例化还是在类被加载时完成,与上边的没有本质区别。优缺点都一样。

懒汉式

懒汉第一式——判断单例对象为null

package 单例模式._3_懒汉式_线程不安全;

// 测试类,测试调用
public class SingletonTest {
	public static void main(String[] args) {
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance==instance2);
	}
}

// 懒汉式(线程不安全)
class Singleton{
	
	// 1. 构造函数私有化,其他类不能new
	private Singleton() {}
	// 2. 生命单例对象
	private static Singleton instance;
	// 3. 返回实例对象。。
	/*
	 * 提供一个公有的静态方法
	 * 	1. 如果单例对象为null则实例化后返回
	 * 	2. 否则直接返回单例对象
	 */
	public static Singleton getInstance() {
		if(instance==null) {
			instance=new Singleton();
		}
		return instance;
	}
}
输出结果:
true
分析:
  1. 为了避免内存资源浪费,在调用时进行判断,如果instane是null,即没有没实例化。就进行实例化并返回instane。否则直接返回instane。
  2. 在多线程是,如果多个饿线程,同时进行instance==null判断,会同时进入到if语句里边,实例化出多个对象。而正常的项目,一般都是多线程的。
优点:

可以懒加载,当需要用到单例对象的时候才去实例化。节省内存资源。

缺点:
  1. 线程不安全,可能被实例化多次。
  2. 不能防止,反射后,可以new。
小结:

正规项目,不建议使用。

懒汉第二式——同步方法

package 单例模式._4_懒汉式_线程安全_同步方法;

// 测试类,调用测试
public class SingletonTest {
	public static void main(String[] args) {
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance==instance2);
	}
}

// 懒汉式(线程安全,同步方法)
class Singleton{
	
	// 1. 构造函数私有化,其他类不能new
	private Singleton() {}
	// 2. 声明单例对象
	private static Singleton instance;
	// 3. 提供一个公有的静态方法返回实例对象,加入同步处理代码(synchronized),解决线程安全问题。
	public static synchronized Singleton getInstance() {
		if(instance==null) {
			instance=new Singleton();
		}
		return instance;
	}
}
输出结果:
true
分析:
  1. synchronized,是一种锁,可以保证,每次只有一个线程在调用。
优点:
  1. 可以懒加载,当需要用到单例对象的时候才去实例化。节省内存资源。
  2. 线程安全,可以防止多线程同时执行实例化。
缺点:
  1. 效率太低,因为synchronized是加在方法上的,多个线程,调用时需要排队等待。
  2. 不能防止,反射后,可以new。
小结:

正规项目,不建议使用。

懒汉第三式——同步代码块

package 单例模式._5_懒汉式_线程安全_同步代码块;

// 测试类,测试调用
public class SingletonTest { 
	public static void main(String[] args) {
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance==instance2);
	}
}

// 懒汉式(线程不安全)
class Singleton{

	// 1. 构造函数私有化,其他类不能new
	private Singleton() {}
	// 2. 生命单例对象
	private static Singleton instance;
	/*
	 * 3. 提供一个公有的静态方法,加入同步处理代码(synchronized)
	 * 	3.1 未解决线程安全问题
	 * 	3.2 不行,但是有人这么写过
	 * 	3.3 之所以提出来,是因为它想提高同步效率
	 */
	public static Singleton getInstance() {
		if(instance==null) {
			synchronized(Singleton.class) {
				instance=new Singleton();
			}
		}
		return instance;
	}
}
输出结果:
true
分析:
  1. 在 if 判断时,就可能有多个线程进来,if 里边的同步代码块就失去了意义。
优点:
  1. 可以懒加载。
缺点:
  1. 多线程不安全
  2. 不能防止,反射后,可以new。
小结:

这种写法没有确保线程安全,是一种错误的写法,但是有的人不知道,就是这样写的。

双重检查

package 单例模式._6_双重检查;

// 测试类,测试调用
public class SingletonTest {
	public static void main(String[] args) {
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance==instance2);
	}

}

// 懒汉式(线程安全)
class Singleton{
	// 1. 构造函数私有化,其他类不能new
	private Singleton() {}
	// 2. 生命单例对象
	private static Singleton instance;
	// 3.提供一个公有的静态方法返回实例对象,加入同步处理代码(synchronized),解决线程安全问题
	public static synchronized Singleton getInstance() {
		if(instance == null) {
			synchronized(Singleton.class) {
				if(instance==null) {
					instance=new Singleton();
				}
			}
		}
		return instance;
	}
}
输出结果:
true
分析:
  1. 第一个 if 确保单例对象还没进行实例化,此时可能有对各线程进入。之后有 synchronized 确保第二个 if 同时只能被一个线程执行,当第一个线程进入后,判断,instance 为 null 然后实例化,离开同步代码块。接下来的线程进入后,判断,instance 不为 null 不进行实例化,直接离开同步代码块。
  2. 再接下来,其他线程来到第一个 if 后,intance 已经不为 null,直接返回。
优点:
  1. 可以懒加载。
  2. 线程安全。
  3. 效率高。
缺点:
  1. 不能防止,反射后,可以new。
小结:

在实际开发中,建议使用。

静态内部类

package 单例模式._7_静态内部类;

// 测试类,测试调用
public class SingletonTest {
	public static void main(String[] args) {
		Singleton instance = Singleton.getInstance();
		Singleton instance2 = Singleton.getInstance();
		System.out.println(instance==instance2);
	}

}

// 静态内部类,
class Singleton{
	// 1. 构造函数私有化,其他类不能new
	private Singleton() {}
	// 2. 写一个静态内部类,该类中有一个静态属性Singleton INSTANE
	private static class SingletonInstance{
		private static final Singleton INSTANE = new Singleton();
	}
	// 3. 提供一个公有的静态方法,加入同步处理代码(synchronized),解决线程安全问题
	public static synchronized Singleton getInstance() {
		return SingletonInstance.INSTANE;
	}
}
输出结果:
true
分析:
  1. 内部类在加载时,不会被加载,只有在调用时才会被实例化。
优点:
  1. 可以懒加载。
  2. 线程安全。
  3. 效率高。
缺点:
  1. 不能防止,反射后,可以new。
小结:

在实际开发中,建议使用。

静态内部类

package 单例模式._8_枚举;

// 测试类,测试调用
public class SingletonTest {
	public static void main(String[] args) {
		Singleton instance = Singleton.INSTANCE;
		Singleton instance2 = Singleton.INSTANCE;
		System.out.println(instance==instance2);
		instance.say();
	}

}

// 枚举,不可以被反射调用
enum Singleton{
	INSTANCE;
	public void say() {
		System.out.println("可以写方法哦");
	}
}
输出结果:
true
分析:
  1. 只有枚举可以防止反射!!
优点:
  1. 可以懒加载。
  2. 线程安全。
  3. 效率高。
缺点:

小结:

在实际开发中,建议使用。

总结

方式懒加载线程安全反射安全效率
饿汉式(静态变量)××
饿汉式(静态代码块)××
懒汉式(if 判断单例对象是否呗实例化)××
懒汉式(同步方法)×
懒汉式(同步代码块)××
双重检查×
静态内部类×
枚举

个人学习总结,有不对的地方,希望大佬指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值