Android单例模式详解

10 篇文章 0 订阅

Android以及Java开发中单例模式是最常用的设计模式,今天来谈谈平时使用的单例模式。

1.静态内部类单例 - 推荐

/**
 * 测试单例 - 静态内部类
 *
 * @author zhukui
 */
public class TestInstance {

    /**
     * 单例调用
     */
    public static TestInstance getInstance() {
        return Holder.INSTANCE;
    }

    /**
     * 静态内部类
     */
    private static class Holder {
        private static final TestInstance INSTANCE = new TestInstance();
    }

    /**
     * 私有构造器
     */
    private TestInstance() {

    }

    /**
     * 测试调用方法
     */
    private void test(){
        //调用
        TestInstance.getInstance();
    }
}

特点:线程安全,调用效率高,可以延时加载。

延时加载原理:

外部类被加载时不会加载静态内部类,不被加载则不占用内存,当外部类调用getInstance方法时,才加载静态内部类,也就是内部类方法什么时候调用什么时候实例化单例,实现了延迟加载。

线程安全原理:

static保证了全局唯一,这里不需要加synchronized关键字,因为JVM保证了一个类的初始化在多线程下被同步加锁,所以保证了线程安全。简单看下,类加载是调用ClassLoader的loadClassOrNull方法,方法中用了synchronized同步锁,静态内部类也是在类加载方法中初始化的,所以保证了线程全。

 protected Class<?> loadClassOrNull(String cn, boolean resolve) {
    synchronized (getClassLoadingLock(cn)) {
        // check if already loaded
        Class<?> c = findLoadedClass(cn);

        return c;
    }

2.饿汉式 - 不推荐

与静态内部类方式不同的是, 类什么时候加载就把单例对象创建出来,不管用不用都创建对象,造成不必要的资源浪费,这种方式简单,如果此单例必定会用到则推荐此种方式,否则不推荐。

/**
 * 测试单例 - 饿汉式
 * <p>
 * 线程安全,调用效率高,但是不能延时加载。
 *
 * @author zhukui
 */
public class TestInstance2 {

    /**
     * 单例调用
     */
    public static TestInstance2 getInstance() {
        return INSTANCE;
    }

    /**
     * 实例 - 不管用不用都创建单例对象
     */
    private static final TestInstance2 INSTANCE = new TestInstance2();

    /**
     * 私有构造器
     */
    private TestInstance2() {

    }

    /**
     * 测试调用方法
     */
    private void test() {
        //调用
        TestInstance2.getInstance();
    }
}

特点:

线程安全,调用效率高,但是不能延时加载。

线程安全原理:

static保证了全局唯一,JVM保证了一个类的初始化在多线程下被同步加锁,所以保证了线程安全。

3.懒汉式 - 不是很推荐

类初始化时,不初始化对象,用的时候才创建,实现了延时加载,单例调用方法用synchronized修饰,调用效率低。

/**
 * 测试单例 - 懒汉式
 * <p>
 * 线程安全,效率不高,延时加载
 *
 * @author zhukui
 */
public class TestInstance3 {

    /**
     * 实例
     * <p>
     * 类初始化时,不初始化对象,用的时候才创建,实现了延时加载
     */
    private static TestInstance3 instance;

    /**
     * 单例调用,方法同步,调用效率低
     */
    public static synchronized TestInstance3 getInstance() {
        if (instance == null) {
            instance = new TestInstance3();
        }
        return instance;
    }

    /**
     * 私有构造器
     */
    private TestInstance3() {
    }

    /**
     * 测试调用方法
     */
    private void test() {
        //调用
        TestInstance3.getInstance();
    }
}

特点:

线程安全,调用效率低,延时加载。

线程安全原理:

synchronized修饰单例调用方法,调用效率低。

4.双重锁单例 - 这种居然是用的最多的

Double CheckLock,简称DCL,也就是双重锁判断机制。

第一次判空,避免单例对象已经创建,每次线程都要走synchronized 代码块,为了提高程序的效率。

第二次判空,防止线程A创建完实例后释放锁,此时线程B获取锁进入同步代码块,没有第二次判断将会重复创建实例。

/**
 * 测试单例 - 双重锁
 * <p>
 * 线程安全,效率一般,延时加载
 *
 * @author zhukui
 */
public class TestInstance4 {

    /**
     * 实例
     * <p>
     * 类初始化时,不初始化这个对象,用的时候才创建实现了延时加载
     */
    private static volatile TestInstance4 instance;

    /**
     * 单例调用
     */
    public static TestInstance4 getInstance() {
        if (instance == null) {
            synchronized (TestInstance4.class) {
                if (instance == null) {
                    instance = new TestInstance4();
                }
            }
        }
        return instance;
    }

    /**
     * 私有构造器
     */
    private TestInstance4() {
    }

    /**
     * 测试调用方法
     */
    private void test() {
        //调用
        TestInstance4.getInstance();
    }
}

特点:

线程安全,调用效率高,延时加载。

线程安全原理:

1.采用类锁synchronized (TestInstance4.class),每个类只有一个 Class 对象,所以每个类只有一个类锁,保证锁的对象唯一性。

2.不同线程将instance值从“主内存”中拷贝到自己的“工作线程内存”,然后修改instance,然后再将新值赋值到主内存,加上volatile关键字,保证多线程有序性、可见性,强制线程每次读取instance值的时候都去“主内存”中取值。

5.枚举方式单例 - 不常用

枚举元素本身就是单例,天生私有构造器,线程安全,调用效率高,不能延时加载(取决于类什么时候加载),可以天然的防止反射和反序列化调用,这里简单说下单例构造方法虽然是private修饰,但是可以通过反射创建单例实例。

/**
 * 测试单例 - 枚举方式
 * <p>
 * 线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用
 *
 * @author zhukui
 */
public enum TestInstance5 {

    /**
     * 枚举元素本身就是单例
     */
    INSTANCE;

    /**
     * 私有构造器 - 天生私有构造,这里只是为了突出而已
     */
    private TestInstance5() {
    }

    /**
     * 添加操作
     */
    public void myOperation() {
        //doSomething
    }

    /**
     * 测试调用方法
     */
    private void test() {
        //调用
        TestInstance5.INSTANCE.myOperation();
    }
}

特点:

线程安全,调用效率高,不能延时加载。

线程安全原理:

JVM保证枚举的线程安全,这里不作更多分析。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值