1 简介
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类
只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
比如: Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session 对象。SessionFactory并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory就够,这是就会使用到单例模式。
2 单例设计模式的方式
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
2.1 饿汉式(静态常量)
/**
* 1、饿汉式(静态常量) 测试 class
*
* @author wsh
* @date 2019-07-12 23:18
*/
public class SingletonTest1 {
public static void main(String[] args) {
//测试
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//ture
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
/**
* 1) 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
* 2) 缺点:在类装载的时候就完成实例化,没有达到LazyLoading的效果。如果从始至终从未使用过这个实例,则
* 会造成内存的浪费
* 3) 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,
* 在单例模式中大 多数都是调用 getInstance 方法,但是导致类装载的原因有很多种,
* 因此不能确定有其他的方式(或者其他的静 态方法)导致类装载,这时候初始化 instance 就没有达到 lazy loading 的效果
* 4) 结论:这种单例模式可用,可能造成内存浪费
*/
class Singleton {
/**
* 1、私有化构造器,防止外部 new 对象
*/
private Singleton() {
}
/**
* 2、对象内部创建对象实例
*/
private final static Singleton INSTANCE = new Singleton();
/**
* 3、提供一个公有的静态方法,获取实例对象
*/
public static Singleton getInstance() {
return INSTANCE;
}
}
结果:
true
instance1.hashCode=1580066828
instance2.hashCode=1580066828
2.2 饿汉式(静态代码块)
/**
* 2、饿汉式(静态变量) 测试 class
*
* @author wsh
* @date 2019-07-12 23:31
*/
public class SingletonTest2 {
public static void main(String[] args) {
//测试
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//ture
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
/**
* 和 test1 一样
* 1) 这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,
* 就执 行静态代码块中的代码,初始化类的实例。优缺点和test1是一样的。
* 2) 结论:这种单例模式可用,但是可能造成内存浪费
*/
class Singleton {
/**
* 1、私有化构造器,防止外部 new 对象
*/
private Singleton() {
}
/**
* 2、对象内部声明对象实例
*/
private static Singleton instance;
/**
* 3、在静态代码块中,创建单例对象
*/
static {
instance = new Singleton();
}
/**
* 4、提供一个公有的静态方法,获取实例对象
*/
public static Singleton getInstance() {
return instance;
}
}
2.3 懒汉式(线程不安全)
/**
* 3、懒汉式(线程不安全)class
*
* @author wsh
* @date 2019-07-16 22:10
*/
public class SingletonTest3 {
public static void main(String[] args) {
System.out.println("懒汉式(线程不安全) test.....");
//测试
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//ture
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {}
/**
* 提供一个公有的静态方法,使用时,才去创建实例
* 懒汉式
* @return
*/
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
利弊分析:
2.4 懒汉式(线程安全,同步方法)
public class SingletonTest4 {
public static void main(String[] args) {
System.out.println("懒汉式2(线程安全, 同步方法) test.....");
//测试
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//ture
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {}
/**
* 提供一个公有的静态方法,使用时,才去创建实例
* 加入同步处理的代码,解决线程安全问题
* 懒汉式
* @return
*/
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2.5 懒汉式(线程安全,同步代码块)
/**
* 5、懒汉式(线程安全,同步方法)class
*
* @author wsh
* @date 2019-07-16 22:20
*/
public class SingletonTest5 {
public static void main(String[] args) {
System.out.println("懒汉式3(线程安全,同步方法) test.....");
//测试
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//ture
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
2.6 双重检查
/**
* 6、双重检查 class
*
* ******** 推荐使用 *******
*
* @author wsh
* @date 2019-07-16 22:25
*/
public class SingletonTest6 {
public static void main(String[] args) {
System.out.println("懒汉式4(双重检查、线程安全、延迟加载) test.....");
//测试
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//ture
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
/**
* 双层检查代码,线程安全问题,懒加载
* 推荐使用这种方式,即保证了线程安全,又顾及了效率
* @return
*/
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2.7 静态内部类
/**
* 7、静态内部类 class
* 1)类装载机制保证初始化实例时只有一个线程
* 2)在Singleton 类被装载时不会立即实例化,需要实例化时,调用gitInstance 方法,才会装载 SingletonInstance 类,完成实例化
* 3)类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行
* 初始化时,别的线程是无法进入的。
*
* ******** 推荐使用 *******
*
* @author wsh
* @date 2019-07-16 22:55
*/
public class SingletonTest7 {
public static void main(String[] args) {
System.out.println("静态内部类 test.....");
//测试
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//ture
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
/**
* 静态内部类,有一个静态属性Singleton
*/
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
/**
* 提供一个静态的公有方法,直接返回静态内部类的静态属性
* @return
*/
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
2.8 枚举
/**
* 8、枚举class
*
* ******** 推荐使用 *******
*
* @author wsh
* @date 2019-07-16 23:08
*/
public class SingletonTest8 {
public static void main(String[] args) {
System.out.println("枚举 test.....");
//测试
Singleton instance1 = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
//ture
System.out.println(instance1 == instance2);
System.out.println("instance1.hashCode=" + instance1.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
instance1.sayOK();
}
}
enum Singleton {
INSTANCE;
public void sayOK() {
System.out.println("ok~");
}
}
3 单例模式在源码中的应用
3.1 JDK中 Runtime 类(饿汉式)
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {
}
/*...........*/
}
4 总结
- 单例模式保证了系统内存中只存在一个对象, 可以节省系统资源,对于频繁创建销毁的对象,可以使用单例模式来提高系统性能;
- 使用建议:
一般情况下,建议使用饿汉方式。
只有在要明确实现 lazy loading 效果时,使用静态内部类方式。
如果涉及到反序列化创建对象时,可以尝试使用枚举方式。
如果有其他特殊的需求,可以考虑使用双检锁方式。
设计模式分为 三种类型,共 23 种
- 创建型模式:
单例模式(Singleton Pattern)
工厂模式(Factory Pattern)
抽象工厂模式(Abstract Factory Pattern)
建造者模式(Builder Pattern)
原型模式(Prototype Pattern) - 结构型模式:
适配器模式(Adapter Pattern)
桥接模式(Bridge Pattern)
过滤器模式(Filter、Criteria Pattern)
组合模式(Composite Pattern)
装饰器模式(Decorator Pattern)
外观模式(Facade Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern) - 行为型模式:
责任链模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
解释器模式(Interpreter Pattern)
迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)
备忘录模式(Memento Pattern)
观察者模式(Observer Pattern)
状态模式(State Pattern)
空对象模式(Null Object Pattern)
策略模式(Strategy Pattern)
模板模式(Template Pattern)
访问者模式(Visitor Pattern)