设计模式(一) singleton--单例模式

常用单例模式

单例模式为Java中最简单的设计模式之一。该模式的目的是用来在程序中创建唯一对象模型。
一些工具类,工厂类等服务性的类,可以考虑设置为单例模式。
单例模式的实现方式有很多,下面来一一列举。

饿汉式

一般情况下推荐使用此种方式,简单实用,且线程安全。
不算缺点的缺点:
不管是否用到,类装载时就进行实例化(一般情况下,不用你写他干啥

/**
 * 饿汉式
 * 类加载到内存后,就实例化一个单例,JVM保证线程安全
 * 简单实用,推荐使用!
 * 唯一缺点:不管用到与否,类装载时就完成实例化
 */
public class Singleton01 {
    private static final Singleton01 INSTANCE = new Singleton01 ();

    private Singleton01 () {};

    public static Singleton01 getInstance() {
        return INSTANCE;
    }

    public void test() {
        System.out.println("test");
    }

    public static void main(String[] args) {
        Singleton01 s1 = Singleton01 .getInstance();
        Singleton01 s2 = Singleton01 .getInstance();
        System.out.println(s1 == s2);
    }
}

懒汉式

用来解决类加载时初始化对象的问题,此方式只有在第一次调用
getInstance()方法时才会初始化对象。
但会造成线程安全问题。(丢西瓜捡芝麻)

/**
 * lazy loading
 * 也称懒汉式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 */
public class Singleton02 {
    private static Singleton02 INSTANCE;

    private Singleton02 () {}

    public static Singleton02 getInstance() {
        if (INSTANCE == null) {
          /*
          	//增加多线程抢占几率 更容器看到错误 
		    try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            */
            INSTANCE = new Singleton02 ();
        }
        return INSTANCE;
    }

    public void test() {
        System.out.println("test");
    }

    public static void main(String[] args) {
    //测试线程不安全
        for(int i=0; i<100; i++) {
            new Thread(()->
                System.out.println(Mgr03.getInstance().hashCode())
            ).start();
        }
    }
}

懒汉式-单锁

在懒汉式的基础上解决线程安全问题,但效率下降

/**
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class Singleton03 {
    private static Singleton03 INSTANCE;

    private Singleton03 () {}

    public static synchronized Singleton03 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton03 ();
        }
        return INSTANCE;
    }

    public void test() {
        System.out.println("test");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Singleton03 .getInstance().hashCode());
            }).start();
        }
    }
}

懒汉式-双重锁判断

懒汉式-单锁版本由于在getInstance()方法上使用 synchronized 上锁,造成效率较低。所以采取双重锁来进行效率提升
说明:
如果使用单锁缩小锁的代码,则仍存在线程不安全情况
eg.

 public static Singleton04 getInstance() {
        if (INSTANCE == null) {
            //妄图通过减小同步代码块的方式提高效率,然后不可行
            synchronized (Singleton04 .class) {
                INSTANCE = new Singleton04 ();
            }
        }
        return INSTANCE;
    }

所以使用双锁来解决问题,人们也认为这个是单例模式的完美模式之一

public class Singleton04 {
    private static volatile Singleton04 INSTANCE; //JIT

    private Singleton04 () {
    }

    public static Singleton04  getInstance() {
        if (INSTANCE == null) {
            
            synchronized (Singleton04 .class) {
            //双重检查
                if(INSTANCE == null) {
                    INSTANCE = new //双重检查();
                }
            }
        }
        return INSTANCE;
    }

    public void test() {
        System.out.println("test");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Singleton04.getInstance().hashCode());
            }).start();
        }
    }
}

静态内部类方式

通过使用静态内部类方式来解决延迟加载问题。
原理:加载外部类时不会加载内部类,private修饰的内部类又只能被外部类使用,而类只会被JVM加载一次,所以是使用JVM保证单例
完美的单例模式之一

/**
 * 静态内部类方式
 * JVM保证单例
 * 加载外部类时不会加载内部类,这样可以实现懒加载
 */
public class Singleton05{

    private Singleton05() {
    }

    private static class Singleton05Holder {
        private final static Singleton05 INSTANCE = new Singleton05();
    }

    public static Singleton05 getInstance() {
        return Singleton05Holder .INSTANCE;
    }

    public void test() {
        System.out.println("test");
    }

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Singleton05 .getInstance().hashCode());
            }).start();
        }
    }


}

枚举方式

使用枚举方式来创建单例,不仅可以解决线程同步,还可以防止反序列化。
最完美单例模式

/**
 * 不仅可以解决线程同步,还可以防止反序列化。
 */
public enum Singleton06{

    INSTANCE;

    public void test() {
		System.out.println("test")
	}

    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Singleton06.INSTANCE.hashCode());
            }).start();
        }
    }

}

以上几种皆为JAVA中的单例模式,在日常使用中,直接使用饿汉式即可,喜欢精益求精的,可以尝试静态内部类、双重锁、枚举的方式、如果涉及到反序列化,推荐枚举方式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值