创建型设计模式之单例模式
单例模式(Singleton Pattern)采取一定的方法保证在整个软件系统中, 对某个类只能存在一个对象实例。使用单例模式减少对象的创建,可提升系统的性能,节省系统的资源。
一般来说,实现的方式就是该类只提供一个静态方法获取其对象实例的入口。
单例模式三种实现方式
饿汉式
在类加载的时候, 对象实例就随之创建。
饿汉式实现方式之静态常量式
package com.inconspicuousy.pattern.singleton.hungry;
class Singleton {
/** 采用静态常量的形式, 在类加载的时候直接进行实例化 */
private static final Singleton SINGLETON = new Singleton();
/** 构造器私有化,只能通过静态方法唯一入口实例化对象 */
private Singleton() {
}
/** 直接返回定义的常量 */
public static Singleton getInstance() {
return SINGLETON;
}
}
/**
* 饿汉式实现方式之静态常量式
* @author inconspicuousy
*/
public class StaticConstantExample {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
// true
System.out.println(instance == instance1);
}
}
饿汉式实现方式之静态代码块
package com.inconspicuousy.pattern.singleton.hungry.block;
class Singleton {
/** 注意, 这里为什么能够用final修饰, 是因为所有的静态代码都会在类构造器中合成一个方法执行 */
private static final Singleton singleton;
/* 通过静态代码块进行初始化 */
static {
singleton = new Singleton();
}
/** 构造器私有化 */
private Singleton() {
}
/** 获取对象入口 */
public static Singleton getInstance() {
return singleton;
}
}
/**
* 饿汉式实现方式之静态代码块
* @author inconspicuousy
*/
public class BlockCodeExample {
public static void main(String[] args) {
// true
System.out.println(Singleton.getInstance() == Singleton.getInstance());
}
}
懒汉式的优缺点
优点
- 实现比较简单,实例随着类的装载而创建,从而避免了线程安全问题。
缺点
- 在类装载的时候就完成实例化,没有达到懒加载的效果。如果从始至终都没有使用过这个实例,则会造成内存内存的浪费。
懒汉式
只有当实例被使用到时候才会被加载, 使用优先级要高于饿汉式实现方式。
懒汉式实现方式之synchronized关键字同步静态方法
package com.inconspicuousy.pattern.singleton.lazy.sync;
class Singleton {
/** 创建静态变量 */
private static Singleton singleton = null;
/** 构造器私有 */
private Singleton() {
}
/** 利用synchronized关键字保证每次只会有一个线程访问到该类方法 */
public synchronized static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
/**
* 利用synchronized关键字实现的懒汉式单例模式
* @author inconspicuousy
*/
public class SyncExample {
public static void main(String[] args) {
// true
System.out.println(Singleton.getInstance() == Singleton.getInstance());
}
}
特点
- 使用
synchronized
关键字锁住静态方法块虽然达到了线程安全问题,但是每次获取对象都会去竞争锁影响程序的性能。优先考虑双重检查式
懒汉式实现方式。
懒汉式实现方式之双重检查
package com.inconspicuousy.pattern.singleton.lazy.check;
import java.util.stream.IntStream;
class Singleton {
/**
* 这里采用 volatile 关键字修饰singleton是因为防止某个线程取得是缓存数据创建多个实例
*
* 被volatile关键字修饰之后, 线程直接会与内存交互, 对其他线程修改的数据是可见的, 每次线程获取singleton的值时都是直接与内存交互
* */
private volatile static Singleton singleton = null;
/** 构造器私有化 */
private Singleton() {
}
/**
* 这里放弃使用synchronized修饰方法的是因为singleton被实例化就会触发一次, 每次都要锁方法影响程序的性能
*
* 将synchronized关键字提到只有当singleton为空时才会锁住, 增强程序的性能
* */
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
// 这里再次对singleton作判空处理是因为防止某些线程已经读取到singleton为空的情况下, 进入阻塞状态, 解除阻塞后实例化singleton
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
/**
* 双重检查实现懒汉式单例模式
* @author inconspicuousy
*/
public class DoubleCheckExample {
public static void main(String[] args) {
// 十个线程获取的值一致
IntStream.rangeClosed(1,10).boxed().forEach(i -> {
new Thread( () -> {
System.out.println(Singleton.getInstance().hashCode());
}).start();
});
}
}
懒汉式实现方式之静态内部类
package com.inconspicuousy.pattern.singleton.lazy.inner;
import java.util.stream.IntStream;
class Singleton {
/** 私有构造器 */
private Singleton() {
}
/** 采用静态内部类的方式, 只有当内部类被加载时实力才会被创建, 实现了懒加载 */
private static class SingletonInstance {
private static final Singleton SINGLETON = new Singleton();
public static Singleton getInstance () {
return SINGLETON;
}
}
/** 只有调用当前方法时, 静态内部类才会被加载 */
public static Singleton getInstance() {
return SingletonInstance.getInstance();
}
}
/**
* 采用懒汉式静态内部类实现的单例模式
* @author inconspicuousy
*/
public class InnerClassExample {
public static void main(String[] args) {
// 所有的线程返回值一致
IntStream.rangeClosed(1,10).boxed().forEach(i -> {
new Thread(() -> System.out.println(Singleton.getInstance().hashCode())).start();
});
}
}
枚举
利用枚举的方式实例化对象
package com.inconspicuousy.pattern.singleton.enumeration;
/**
* 利用枚举类创建对象
*
* 其实枚举的底层创建的实例都是被 static final修饰的
* 本质上是一种饿汉式的特殊形式
* */
enum Singleton {
SINGLETON;
}
/**
* 利用枚举类实现单例模式
*
* @author inconspicuousy
*/
public class EnumExample {
public static void main(String[] args) {
// true
System.out.println(Singleton.SINGLETON == Singleton.SINGLETON);
}
}
单例模式的使用场景
基于单例模式
保证系统内存中该类只会存在一个对象的特点
可以用到如下的场景。
- 需要频繁的进行创建和销毁的对象
- 创建对象耗时太多或者耗费资源太多但又经常用到的对象
工作中比如:工具类对象、频繁访问数据库或者文件的对象(数据源、session工厂)