设计模式(一):单例模式

  1. Singleton(单例模式)

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

    注意:

    1、单例类只能有一个实例。
    2、单例类必须自己创建自己的唯一实例。
    3、单例类必须给所有其他对象提供这一实例。

    模式一:简单实用,但是在Test1被初始化时就创建了Test1的实例对象
    public class Test1 {
    
        //静态变量,在初始化时只实例化一次
        private static final Test1 INSTANCE =new Test1();
    
        //私有的构造方法,拒绝外界通过new的方式实例化Test1
        private Test1(){ }
    
        public static Test1 getInstance(){
            return INSTANCE;
        }
    
        public static void main(String[] args) {
    
            Test1 t1 =Test1.getInstance();
            Test1 t2 =Test1.getInstance();
            System.out.println(t1==t2);
        }
    }
    
    模式二:通过静态代码块方式实例化Test2对象,其实与模式一一样
    public class Test2 {
    
        //静态变量,在初始化时只实例化一次
        private static final Test2 INSTANCE ;
        static {
            INSTANCE=new Test2();
        }
    
        //私有的构造方法,拒绝外界通过new的方式实例化Test1
        private Test2(){ }
    
        public static Test2 getInstance(){
            return INSTANCE;
        }
    
        public static void main(String[] args) {
    
            Test2 t1 = Test2.getInstance();
            Test2 t2 = Test2.getInstance();
            System.out.println(t1==t2);
        }
    }
    
    模式三:懒汉式,虽然解决了懒加载问题,但是出现了线程安全问题
    public class Test3 {
    
        //静态变量,在初始化时只实例化一次
        private static Test3 INSTANCE;
    
        //私有的构造方法,拒绝外界通过new的方式实例化Test1
        private Test3() {
        }
    
        public static Test3 getInstance() {
            if (INSTANCE == null) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Test3();
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Test3.getInstance().hashCode());
                }).start();
            }
        }
    }
    
    模式四:懒汉式虽然可以通过synchronized加锁,但是会带来性能的下降
    public class Test4 {
    
        //静态变量,在初始化时只实例化一次
        private static Test4 INSTANCE;
    
        //私有的构造方法,拒绝外界通过new的方式实例化Test1
        private Test4() {
        }
    
        public static synchronized Test4 getInstance() {
            if (INSTANCE == null) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Test4();
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Test4.getInstance().hashCode());
                }).start();
            }
        }
    }
    
    模式五:通过缩小synchronized范围,但是依然存在线程安全问题
    public class Test5 {
    
        //静态变量,在初始化时只实例化一次
        private static Test5 INSTANCE;
    
        //私有的构造方法,拒绝外界通过new的方式实例化Test1
        private Test5() {
        }
    
        public static  Test5 getInstance() {
            if (INSTANCE == null) {
                //妄图通过减小同步代码块来提高性能,但依然不可行
                synchronized (Test5.class){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Test5();
                }
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Test5.getInstance().hashCode());
                }).start();
            }
        }
    }
    
    模式六:双重判断

如果指令按照顺序执行倒也无妨,但JVM为了优化指令,提高程序运行效率,允许指令重排序。如此,在程序真正运行时以上指令执行顺序可能是这样的:

a)给instance实例分配内存;

b)将instance对象指向分配的内存空间;

c)初始化instance的构造器;

这时候,当线程一执行b)完毕,在执行c)之前,被切换到线程二上,这时候instance判断为非空,此时线程二直接来到return instance语句,拿走instance然后使用,接着就顺理成章地报错(对象尚未初始化)。

具体来说就是synchronized虽然保证了线程的原子性(即synchronized块中的语句要么全部执行,要么一条也不执行),但单条语句编译后形成的指令并不是一个原子操作(即可能该条语句的部分指令未得到执行,就被切换到另一个线程了)

public class Test6 {

    //静态变量,在初始化时只实例化一次
    private static volatile Test6 INSTANCE;

    //私有的构造方法,拒绝外界通过new的方式实例化Test1
    private Test6() {
    }

    public static Test6 getInstance() {
        if (INSTANCE == null) {
            //妄图通过减小同步代码块来提高性能,但依然不可行
            synchronized (Test6.class) {
                //双重判断
                if (INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Test6();
                }
            }
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Test6.getInstance().hashCode());
            }).start();
        }
    }
}
模式七:通过静态内部类方式,JVM保证单例,加载外部类时不会加载内部类,这样可以实现懒加载
public class Test7 {

    //私有的构造方法,拒绝外界通过new的方式实例化Test1
    private Test7() {
    }

    //静态内部类,外部无法访问
    private static class Test7Holder{

        private static Test7 INSTANCE =new Test7();
    }
    public static Test7 getInstance() {
        return Test7Holder.INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(Test7.getInstance().hashCode());
            }).start();
        }
    }
}
模式八:不仅可以解决线程同步,还可以防止反序列化,之前几种方式可以根据class文件通过反射,newInstance根据构造方法得到一个实例,而枚举没有构造方法,所以无法反序列化
public enum Test8 {
    INSTANCE;

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

github: https://github.com/lishanglei/design-patterns.git

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值