目录
单例模式(Singleton):
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现原理:
将构造方法改为私有,并对外提供一个可获得实例的公共静态方法。
应用场景:
- 类只需要一个实例,比如工厂模式的工厂。
- WEB中,点击量、在线人数等计数器。
- 创建一个对象需要消耗的资源过多,如连接数据库。
实例:
实现单例模式的方法总共有八种,下边先展示一个最简单的,解释完单例模式之后再展示其他。
饿汉式
饿汉第一式——静态变量
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
分析:
- 首先从输出结果就可以看出开,调用两次得到的对象是同一个。
- 用private修饰的构造方法,外部类不能访问,也就不能new。只能通过单例类提供的方法获得。
- 单例类将自己作为一个属性,并使用static修饰,在类被加载时就会实例化一次本类,并且不会再次实例化。确保单例类只有一个实例对象。
优点:
简单
缺点:
- 因为是被static修饰的,所以在类加载时就直接完成实例化,会一直占用内存资源。
- 不能防止,反射后,可以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
分析:
- 为了避免内存资源浪费,在调用时进行判断,如果instane是null,即没有没实例化。就进行实例化并返回instane。否则直接返回instane。
- 在多线程是,如果多个饿线程,同时进行instance==null判断,会同时进入到if语句里边,实例化出多个对象。而正常的项目,一般都是多线程的。
优点:
可以懒加载,当需要用到单例对象的时候才去实例化。节省内存资源。
缺点:
- 线程不安全,可能被实例化多次。
- 不能防止,反射后,可以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
分析:
- synchronized,是一种锁,可以保证,每次只有一个线程在调用。
优点:
- 可以懒加载,当需要用到单例对象的时候才去实例化。节省内存资源。
- 线程安全,可以防止多线程同时执行实例化。
缺点:
- 效率太低,因为synchronized是加在方法上的,多个线程,调用时需要排队等待。
- 不能防止,反射后,可以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
分析:
- 在 if 判断时,就可能有多个线程进来,if 里边的同步代码块就失去了意义。
优点:
- 可以懒加载。
缺点:
- 多线程不安全
- 不能防止,反射后,可以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
分析:
- 第一个 if 确保单例对象还没进行实例化,此时可能有对各线程进入。之后有 synchronized 确保第二个 if 同时只能被一个线程执行,当第一个线程进入后,判断,instance 为 null 然后实例化,离开同步代码块。接下来的线程进入后,判断,instance 不为 null 不进行实例化,直接离开同步代码块。
- 再接下来,其他线程来到第一个 if 后,intance 已经不为 null,直接返回。
优点:
- 可以懒加载。
- 线程安全。
- 效率高。
缺点:
- 不能防止,反射后,可以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
分析:
- 内部类在加载时,不会被加载,只有在调用时才会被实例化。
优点:
- 可以懒加载。
- 线程安全。
- 效率高。
缺点:
- 不能防止,反射后,可以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
分析:
- 只有枚举可以防止反射!!
优点:
- 可以懒加载。
- 线程安全。
- 效率高。
缺点:
无
小结:
在实际开发中,建议使用。
总结
方式 | 懒加载 | 线程安全 | 反射安全 | 效率 |
---|---|---|---|---|
饿汉式(静态变量) | × | √ | × | 高 |
饿汉式(静态代码块) | × | √ | × | 高 |
懒汉式(if 判断单例对象是否呗实例化) | √ | × | × | 高 |
懒汉式(同步方法) | √ | √ | × | 低 |
懒汉式(同步代码块) | √ | × | × | 高 |
双重检查 | √ | √ | × | 高 |
静态内部类 | √ | √ | × | 高 |
枚举 | √ | √ | √ | 高 |
个人学习总结,有不对的地方,希望大佬指正。