单例模式
创建型模式
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
介绍
意图: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决: 一个全局使用的类频繁地创建与销毁。
何时使用: 当您想控制实例数目,节省系统资源的时候。
如何解决: 判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码: 构造函数是私有的。
具体实现
1.饿汉式(静态变量)
public class SingletonTest01 {
public static void main(String[] args) {
Singleton singleton01 = Singleton.getInstance();
Singleton singleton02 = Singleton.getInstance();
System.out.println(singleton01 == singleton02); //true
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
}
}
/**
* 饿汉式(静态变量)
*/
class Singleton {
//1.构造器私有化, 外部不能new
private Singleton() {
}
//2.创建实例
private final static Singleton instance = new Singleton();
//3.提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}优点:
这种写法比较简单,就是在类装载的时候就完成了实例化。避免了线程同步问题 。
缺点:
在类装载的时候就完成了实例化,没有达到Lazy Loading的效果。如果从始至终都没有使用过这个实例 , 则会造成内存的浪费。
总结:
这种单例模式可以使用 ,可能会造成内存浪费。
2.饿汉式(静态代码块)
public class SingletonTest02 {
public static void main(String[] args) {
Singleton singleton01 = Singleton.getInstance();
Singleton singleton02 = Singleton.getInstance();
System.out.println(singleton01 == singleton02); //true
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
}
}
/**
* 饿汉式(静态代码块)
*/
class Singleton {
//1.构造器私有化,外部不能new
private Singleton() {
}
//2.声明实例
private final static Singleton instance;
//3.创建实例
static {
instance = new Singleton();
}
//4.提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}总结:
这种方式的优缺点和第一种类似,只不过将类的实例化过程放在了静态代码块中,依然可能造成内存浪费。
3.懒汉式(线程不安全)
public class SingletonTest03 {
public static void main(String[] args) {
Singleton singleton01 = Singleton.getInstance();
Singleton singleton02 = Singleton.getInstance();
System.out.println(singleton01 == singleton02); //true (有可能为false)
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
}
}
/**
* 懒汉式(线程不安全)
*/
class Singleton {
private Singleton() {
}
public static Singleton instance;
//提供一个静态的公有方法,当使用到该方法时,才会创建instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}优点:
起到了Lozy Loading的效果,但是只能在单线程的环境下使用。
缺点:
如果在多线程环境下,大概率会产生多个实例。所以在多线程环境下不要使用这种方式。
总结:
实际开发中不要使用这种方式。
4.懒汉式(线程安全)
public class SingletonTest04 {
public static void main(String[] args) {
Singleton singleton01 = Singleton.getInstance();
Singleton singleton02 = Singleton.getInstance();
System.out.println(singleton01 == singleton02); //true
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
}
}
/**
* 懒汉式(线程安全)
*/
class Singleton {
private Singleton() {
}
public static Singleton instance;
//提供一个静态的公有方法,当使用到该方法时,才会创建instance。
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}优点:
解决了线程不安全的问题。
缺点:
效率太低,因为是对整个方法加上了线程同步,其实只要在new的时候考虑线程同步就行了,这种方法不推荐使用。
总结:
在实际开发中 ,不推荐使用这种方式 。
5.懒汉式(双重检查锁)
public class SingletonTest05 {
public static void main(String[] args) {
Singleton singleton01 = Singleton.getInstance();
Singleton singleton02 = Singleton.getInstance();
System.out.println(singleton01 == singleton02); //true
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
}
}
/**
* 懒汉式(线程安全,双重检查锁)
*/
class Singleton {
private Singleton() {
}
public static volatile Singleton instance;
//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}优点:
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
总结:
在实际开发中,推荐使用这种单例设计模式 。
6.静态内部类
public class SingletonTest06 {
public static void main(String[] args) {
Singleton singleton01 = Singleton.getInstance();
Singleton singleton02 = Singleton.getInstance();
System.out.println(singleton01 == singleton02); //true
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
System.out.println("singleton01.hashCode = " + singleton01.hashCode()); //1625635731
}
}
/**
* 静态内部类
*/
class Singleton {
private Singleton() {
}
//静态内部类中包含一个静态属性 Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}优点:
- 这种方式能达到双检锁方式一样的功效,但实现更简单。
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
- 静态内部类的方式在Singleton类被装载的时候并不会立即初始化 , 而是在需要实例化时,调用
getInstance方法才会装载SingletonInstance类,从而完成Singleton的实例化 。 - 类的静态属性只会在第一次加载类的时候进行初始化,所以这里JVM保证了线程安全性,在类进行初始化的时候 , 别的线程是无法进入的 。
总结:
避免了线程不安全,利用静态内部类的特点实现了延迟加载,效率高 ,推荐使用 。
7.枚举
public class SingletonTest07 {
public static void main(String[] args) {
Singleton instance1 = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance1 == instance2);
}
}
/**
* 枚举
*/
enum Singleton {
INSTANCE;
}优点:
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法 。它更简洁,自动支持序列化机制,绝对防止多次实例化 。
这种方式是《 Effective Java 》作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题 ,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
总结:
如果涉及到反序列化对象的时候可以采用采用这种方式,平常使用可以采用第五种。

