单例几种实现方式和优缺点,最佳实现方案记录

本文详细探讨了单例模式的多种实现方式,包括饿汉式、懒汉式及其升级版,静态内部类和枚举方式。对比分析了各种实现的优缺点,如对象持久存在、多线程安全性和性能开销等问题。
package com.innext.n.xyh;

/**
 * @Author: baiyongchen
 * @CreateDate: 2019/3/15 15:51
 * @Description: 单例几种类型,缺点比较。最终实现方案
 */
public class Person {

    //私有化保证外部不能随便通过new创建对象
    private Person() {

    }

    //region  饿汉式
    /**
     * 静态成员变量mPerson1在Person这个类加载的时候就初始化,创建了Person对象,
     * 并且只存在一个;通过Person.getInstance1()方法可以获取该实例对象
     */
    private static Person mPerson1 = new Person();

    public static Person getInstance1() {
        return mPerson1;
    }
    //缺点:Person对象会一直存在。
    //endregion


    //region 懒汉式
    /**
     * 默认不创建对象,当调用getInstance2()时,创建对象。
     */
    private static Person mPerson2;

    public static Person getInstance2() {
        if (mPerson2 == null) {
            mPerson2 = new Person();
        }
        return mPerson2;
    }
    //缺点:多线程会创建多个对象
    //endregion


    //region 懒汉式升级版1
    /**
     * 给getInstance3方法加了关键字synchronized,保证创建对象的时候只有一个调用者
     */
    private static Person mPerson3;

    public synchronized static Person getInstance3() {
        if (mPerson3 == null) {
            mPerson3 = new Person();
        }
        return mPerson3;
    }
    //缺点:锁会导致时间开销大。
    //endregion

    //region 懒汉式升级版2
    /**
     * 给mPerson4 = new Person()语句加锁synchronized,
     */
    private static Person mPerson4;

    public static Person getInstance4() {
        if (mPerson4 == null) {
            synchronized (Person.class) {
                mPerson4 = new Person();
            }
        }
        return mPerson4;
    }
    //缺点:设现有线程A和B,在某个时刻两个线程都通过了判空语句但都没有取到锁资源,
    //      然后线程A先取得锁资源进入临界区(被锁的代码块),创建了一个对象,然后退
    //      出临界区,释放锁资源。接着线程B取得锁资源进入临界区,开始创建对象,退
    //      出临界区,释放锁资源,这个时候对象也会出现多个
    //endregion

    //region 懒汉式升级版3
    /**
     * 双重校验锁
     */
    private static volatile Person mPerson5;

    public static Person getInstance5() {
        if (mPerson5 == null) {
            synchronized (Person.class) {
                if (mPerson5 == null) {
                    mPerson5 = new Person();
                }
            }
        }
        return mPerson5;
    }
    /*
     * 首先,方法锁改成代码块锁,减少锁的范围;其次第一次判空,在单线程的情况下提升了效率,但此时如果同时存在两个线程并发情况,即都判空成功,
     * 接下来会由锁内的第二次判空来过滤。还是刚才的例子,假设现有线程A和B,在某个时刻两个线程都通过了第一次判空语句但都没有取到锁资源。然后线
     * 程A先取得锁资源进入临界区(被锁的代码块),执行第二次判空语句,判空成功,创建了一个对象,然后退出临界区,释放锁资源。接着线程B取得锁资源
     * 进入临界区,执行判空语句发现不通过,直接退出临界区,释放锁资源。
     *
     * 另外在成员变量mPerson5前面加了一个volatile关键字
     */
    //endregion


    //region 静态内部类实现的单例模式 既保证了只存在一个单例,又保证了线程安全
    public static class SinglePerson {
        private static Person mPerson6 = new Person();
    }

    public static Person getmPerson6() {
        return SinglePerson.mPerson6;
    }

    /**
     * 当外部类Person被加载时,其静态内部类SinglePerson不会被加载,所以它的成员变量mPerson6是不会被初始化的,
     * 只有当调用Person.getInstance6()方法时,才会加载SinglePerson并且初始化其成员变量,而类加载时是线程安全的,
     * 这样既保证了延迟加载,也保证了线程安全,同时也简化了代码量,一举三得!
     */
    //endregion

    //region 序列化创建单例
    //无亲自写过
    //endregion

    //region 《Effective Java》一书中,作者Joshua Bloch提倡可以采用枚举的方式   最佳方案
    public static Person getmPerson7() {
        return EnumSinglePerson.INSTANCE.getInstance();
    }

    private enum EnumSinglePerson {
        INSTANCE;
        private Person mPerson7;

        EnumSinglePerson() {
            mPerson7 = new Person();
        }

        public Person getInstance() {
            return mPerson7;
        }
    }
    //endregion
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值