设计模式---单例模式Singleton

博客指出不用加锁也能实现懒加载,为信息技术领域的开发提供了一种新的思路,避免了加锁可能带来的性能问题等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

/**
 * 饿汉式
 *  类加载到内容后,就实例化一个实例,
 *  JVM保证线程安全: JVM保证每一个Class只会load到内存一次,static关键字是在Class load到内存之后马上就行初始化,也保证初始化一次。
 *  简单实用,推荐使用
 *  唯一缺点:不管用到与否,类装载时就完成实例化
 *
 */
public class Mgr01 {

    private static final Mgr01 INSTANCE = new Mgr01();

    /**
     * 注意这里的构造方法使用private修饰,说明该构造方法只有该类自己才能调用。别人无法调用。
     */
    private Mgr01(){
    }

    public static Mgr01 getInstance(){
        return INSTANCE;
    }

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

    public static void main(String[] args) {
        Mgr01 m1 = Mgr01.getInstance();
        Mgr01 m2 = Mgr01.getInstance();
        System.out.println(m1 == m2);
    }
}
/**
 * loay loading 懒汉模式
 * 虽然达到了按需初始化的目的,但却带来线程不安全的问题
 * 可以通过synchronized解决,但也带来效率下降
 */
public class Mgr03 {

    private static Mgr03 INSTANCE;

    private Mgr03(){
    }

    public static synchronized Mgr03 getInstance(){

        if(null == INSTANCE){
            try {
                Thread.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }

            INSTANCE = new Mgr03();
        }

        return INSTANCE;
    }

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

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {

            //lambar表达式是对只能有一个方法的匿名内部类的简化写法
//            new Thread(()->{
//                System.out.println(Mgr03.getInstance().hashCode());
//            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr03.getInstance().hashCode());
                }
            }).start();
        }
    }
}

public class Mgr06 {

     //这里必须要加上volatile Java虚拟机内部优化,语句重排。 如果不加volatile,会在没有初始化的时候就返回这个INSTANCE
    private static volatile Mgr06 INSTANCE;

    private Mgr06(){
    }

    public  static Mgr06 getInstance(){

		//这里的第一次判断是有必要的,大多数情况下,INSTANCE不为空,就直接返回了,不需要上锁了。
        if(null == INSTANCE){
            //双重检查
            synchronized(Mgr06.class){
                if(null == INSTANCE) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    INSTANCE = new Mgr06();
                }

            }
        }

        return INSTANCE;
    }

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

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr06.getInstance().hashCode());
                }
            }).start();
        }
    }
}
**
 * 静态内部类方式--最完美的写法
 * JVM保证单例,虚拟机加载一个class的时候,只加载一次。所以里面的Mgr07Holder只加载一次,里面的INSTANCE只加载一次。因此它只有一个对象
 * 加载外部类时不会加载内部类,这样可以实现懒加载
 */
public class Mgr07 {

    private Mgr07(){
    }

    //一个类的静态内部类在外面类被加载的时候,里面的静态内部类不会被加载,只有在调用getInstance的时候才会被加载。
    private static class Mgr07Holder{
        private final static Mgr07 INSTANCE = new Mgr07();
    }

    public static Mgr07 getInstance(){
        return Mgr07Holder.INSTANCE;
    }

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

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                System.out.println(Mgr07.getInstance().hashCode());
            }).start();
        }
    }
}
/**
 * 不仅可以解决线程同步,还可以防止反序列化
 * 做单例的时候为什么要防止反序列化:
 *    Java的反射是可以通过一个class文件把整个class load到内存,然后new一个实例出来
 *    那么前面的写法都可以找到class文件通过反射,然后new一个实例出来, 通过反序列化的方式
 *    枚举单例不会被反序列化的原因是枚举类是没有构造方法的(反编译之后枚举是一个abstract class)。
 * 枚举单例
 */
public enum  Mgr08 {

   INSTANCE;

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

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

不用加锁也能实现懒加载

/**
 * 线程安全的单例模式
 * 阅读文章  http://www.cnblogs.com/xudong-bupt/p/3433643.html
 * 更好的方式采用下面的方式(使用内部类的单例模式),既不用加锁,也能实现懒加载,当需要的时候才加载。不用加锁也能实现懒加载
 */

import java.util.Arrays;

public class Singleton {

    private Singleton(){
        System.out.println("single");
    }

    private static class Inner{
        private static Singleton s = new Singleton();   //内部类里new对象
    }

    public static Singleton getSingle(){
        return  Inner.s;    //返回的是内部类的静态对象。
    }

    public static void main(String[] args) {
        Thread[] ths = new Thread[200];

        for (int i = 0; i < ths.length; i++) {
            ths[i] = new Thread(()->{
                Singleton.getSingle();
            });
        }

        Arrays.asList(ths).forEach(o->o.start());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值