前言

作为一家公司的Android技术主管,面试是一件比拟爽的事,一般来说我面到的都是程度不如我的(次要公司面试岗位不是很高,一般来说是中级或高级工程师),那么作为主管的我,对于人员的筛选上,设计模式比拟问的多。然而真正能说的出设计模式的人少之又少。很难让我称心。尤其是及其罕用的单例模式。

单例是什么?

是一种对象创立模式,能够确保我的项目中一个类只产生一个实例。

益处

对于频繁应用的对象能够缩小创建对象所破费的工夫,这对于重量级对象来说,几乎是福音。因为new的缩小,对系统内存应用频率也会升高,缩小GC的压力,并缩短GC进展工夫,这也会缩小Android我的项目的UI卡顿。

java的单例及其波及的知识点

  1. 饿汉模式
public class TestSingleton {    private static final TestSingleton testSingleton = new TestSingleton();    private TestSingleton(){    }    public static TestSingleton getInstance(){        return testSingleton;    }}

细节我就不多写了,大家都应该晓得,构造函数为private,用getInstance来获取实例

  1. 懒汉模式
public class TestSingleton {    private static TestSingleton testSingleton;    private TestSingleton(){    }    public static TestSingleton getInstance(){        if(testSingleton==null){            testSingleton = new TestSingleton();        }        return testSingleton;    }}

比饿汉式的长处在于用时再加载,比拟重量级的单例,就不实用与饿汉了。

  1. 线程平安的懒汉模式
public class TestSingleton {    private static TestSingleton testSingleton;    private TestSingleton(){    }    public static TestSingleton getInstance(){        if(testSingleton==null){            synchronized (TestSingleton.class){                testSingleton = new TestSingleton();            }        }        return testSingleton;    }}

能够看到的是比下面的单例多了一个对象锁,着能够保障在创建对象的时候,只有一个线程可能创建对象。

  1. 线程平安的懒汉模式-DCL双重查看锁机制
public class TestSingleton {    private static volatile TestSingleton testSingleton;    private TestSingleton(){    }    public static TestSingleton getInstance(){        if(testSingleton==null){            synchronized (TestSingleton.class){                if(testSingleton==null){                    testSingleton = new TestSingleton();                }            }        }        return testSingleton;    }}

双重查看,同步块加锁机制,保障你的单例可能在加锁后的代码里判断空,还有减少了一个volatile 关键字,保障你的线程在执行指令时候按程序执行。这也是市面上见的最多的单例。

敲黑板!!知识点:原子操作、指令重排。

什么是原子操作?

简略来说,原子操作(atomic)就是不可分割的操作,在计算机中,就是指不会因为线程调度被打断的操作。

m = 6; // 这是个原子操作

如果m原先的值为0,那么对于这个操作,要么执行胜利m变成了6,要么是没执行m还是0,而不会呈现诸如m=3这种两头态——即便是在并发的线程中。

而,申明并赋值就不是一个原子操作:

int n = 6; // 这不是一个原子操作

对于这个语句,至多有两个操作:

申明一个变量n
给n赋值为6
这样就会有一个中间状态:变量n曾经被申明了然而还没有被赋值的状态。

在多线程中,因为线程执行程序的不确定性,如果两个线程都应用m,就可能会导致不稳固的后果呈现。

什么是指令重排?

简略来说,就是计算机为了进步执行效率,会做的一些优化,在不影响最终后果的状况下,可能会对一些语句的执行程序进行调整。

int a ; // 语句1

a = 8 ; // 语句2

int b = 9 ; // 语句3

int c = a + b ; // 语句4

失常来说,对于程序构造,执行的程序是自上到下,也即1234。

然而,因为指令重排的起因,因为不影响最终的后果,所以,理论执行的程序可能会变成3124或者1324。

因为语句3和4没有原子性的问题,语句3和语句4也可能会拆分成原子操作,再重排。

也就是说,对于非原子性的操作,在不影响最终后果的状况下,其拆分成的原子操作可能会被重新排列执行程序。

次要在于testSingleton = new TestSingleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大略做了上面 3 件事件。

给 testSingleton 分配内存
调用 testSingleton 的构造函数来初始化成员变量,造成实例
将testSingleton 对象指向调配的内存空间(执行完这步 testSingleton 才是非 null 了)
然而在 JVM 的即时编译器中存在指令重排序的优化。也就是说下面的第二步和第三步的程序是不能保障的,最终的执行程序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行结束、2 未执行之前,被线程二抢占了,这时 testSingleton 曾经是非 null 了(但却没有初始化),所以线程二会间接返回 instance,而后应用,而后牵强附会地报错。

举荐后两种来实现单例

  1. 动态外部类来实现单例

    public class TestSingleton { private TestSingleton(){ } public static TestSingleton getInstance(){     return TestSingletonInner.testSingleton; } private static class TestSingletonInner{     static final TestSingleton testSingleton = new TestSingleton(); }}

    static 保证数据独一份

final 初始化实现后不能被批改,线程平安。

敲黑板!!知识点:java在加载类的时候不会将其外部的动态外部类加载,只有在应用该外部类办法时才被调用。这显著是最好的单例,并不需要什么锁一类的机制。

利用了类中动态变量的唯一性

长处:

jvm自身机制保障线程平安。
synchronized 会导致性能问题。
TestSingletonInner 是公有的,除了通过TestSingleton 拜访,没有其余拜访的可能性。

  1. 枚举单例
public enum  TestSingleton {    INSTANCE;    public void toSave(){    }}

应用TestSingleton.INSTANCE.toSave();

创立枚举实例的过程是线程平安的,所以这种写法也没有同步的问题。如果你要本人增加一些线程平安的办法,记得控制线程平安哦。

长处:写法简略/线程平安

kotlin的单例

  1. 饿汉式实现
    object SingletonDemo
  2. 懒汉式
class Singleton private constructor() {    companion object {        var instance: Singleton? = null            get() {                if (field == null) {                    field = Singleton()                }                return field            }        private set    }}
  1. 线程平安的懒汉式
class Singleton private constructor() {    companion object {        var instance: Singleton? = null            @Synchronized            get() {                if (field == null) {                    field = Singleton()                }                return field            }            private set    }}
  1. 双重校验锁式
class Singleton private constructor() {    companion object {        val instance: Singleton by lazy {        Singleton() }    }}
  1. 动态外部类式
class Singleton private constructor() {    companion object {        val instance = SingletonHolder.holder    }    private object SingletonHolder {        val holder= Singleton()    }}

我集体在用kotlin单例的话我还是喜爱间接用lazy,不便,嘻嘻嘻

Android源码中的单例

通常咱们会用context.getSystemService(String name)来获取一些零碎服务
如下:

ActivityManager  mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);  

例如:LayoutInflater

package android.view;public static LayoutInflater from(Context context) {        LayoutInflater LayoutInflater =                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        if (LayoutInflater == null) {            throw new AssertionError("LayoutInflater not found.");        }        return LayoutInflater;    }

Context 局部源码

public abstract class Context {    public abstract Object getSystemService(@ServiceName @NonNull String name);}

通过剖析activity的启动流程能够晓得,Context的性能的具体实现是在ContextImpl.java

class ContextImpl extends Context {...    @Override    public Object getSystemService(String name) {        return SystemServiceRegistry.getSystemService(this, name);    }}

而后持续SystemServiceRegistry.getSystemService(this, name):

final class SystemServiceRegistry {    ...//用来getSystemService的容器,外面寄存的是ServiceFetcher<?>private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =            new HashMap<String, ServiceFetcher<?>>();...//动态代码块,第一次加载时执行,而且只会执行一次,保障了注册的服务的唯一性。    static {        registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,                new CachedServiceFetcher<AccessibilityManager>() {            @Override            public AccessibilityManager createService(ContextImpl ctx) {                return AccessibilityManager.getInstance(ctx);            }});        registerService(Context.DOWNLOAD_SERVICE, DownloadManager.class,                new CachedServiceFetcher<DownloadManager>() {            @Override            public DownloadManager createService(ContextImpl ctx) {                return new DownloadManager(ctx);            }});        ...//还有很多服务注册    }      ...//动态代码块中调用这个办法,把服务名和创立的服务对应放在容器中,实现单例。      private static <T> void registerService(String serviceName, Class<T> serviceClass,            ServiceFetcher<T> serviceFetcher) {        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);    }  ...  public static Object getSystemService(ContextImpl ctx, String name) {        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);        return fetcher != null ? fetcher.getService(ctx) : null;    }  ...}

外面还不是间接拿到服务,而是调用了fetcher.getService(ctx)来获取服务。看看

ServiceFetcher<?>:static abstract interface ServiceFetcher<T> {        T getService(ContextImpl ctx);    }

这是个接口,看下面的动态代码块外面的办法发现注册服务的时候都是用的CachedServiceFetcher这个类:

static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {        private final int mCacheIndex;        public CachedServiceFetcher() {            mCacheIndex = sServiceCacheSize++;        }        @Override        @SuppressWarnings("unchecked")        public final T getService(ContextImpl ctx) {//ctx.mServiceCache是获取一个数组:new Object[sServiceCacheSize];//数组的长度就是构造方法中的那个变量,每注册一个服务,就会new一个对应的CachedServiceFetcher,而后数组长度就+1。第一次获取到这个数组必定是个空数组            final Object[] cache = ctx.mServiceCache;            synchronized (cache) {                // Fetch or create the service.                Object service = cache[mCacheIndex];//第一次获取这个服务的时候,数组是空的 ,所以service == null为TRUE。                if (service == null) {//调用注册时实现的createService办法,把生成的具体服务放在数组对应下标中,//之后就间接从数组中获取了。实现了单例。                    service = createService(ctx);                    cache[mCacheIndex] = service;                }                return (T)service;            }        }      //  在动态代码块中实现        public abstract T createService(ContextImpl ctx);    }

外面有个形象办法,须要实例化的时候实现。在动态代码块中的办法都实现了这个createService(ContextImpl ctx)办法,并且返回了对应的服务。