Android 设计模式—单例模式记要

单例模式

定义:(定义都是抽象的,无需过度在意其意义,设计模式这种东西只可意会不可言传)确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

单例模式写法有很多种,因地制宜就行,但关键点都是在并发问题上:

1.private构造函数

2.public static静态公有方法

3.保证线程安全,适应多线程并发访问:(2个关键点)

        * 同步锁synchronized修饰符:避免同时被多个线程访问,防止线程不同步,保证并发情况下的原子性,可见性,有序性

          * Volatile修饰符:本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从内存中读取(它修饰的变量的读写操作都必须在内存中进行),保证了多线程共享变量的可见性,原子性(需条件),有序性(需条件))。

4.保证访问性能(例如单例每次访问都进行synchronized判断影响性能)

5.相关:因为单例涉及并发,涉及synchronized和Volatile修饰符,这2者都关系到CUP线程与内存交互,java内存模拟。不论是synchronized还是Volatile都是对下面3个保证性问题进行的操作,只是在某些情况下适用或者不适用。

        5.1.Java内存模拟JMM(JMM定义了java虚拟机JVM在内存中的工作方式)

JMM定义了多线程之间共享变量的可见性,一个线程对共享变量的写入何时对另一个线程可见,以及如何在需要的时候对共享变量进行同步。

        5.2.并发编程必须的3个保证性问题:原子性问题,可见性问题,有序性问题

          *原子性:一个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就不执行。

理解场景:A向B转账500元操作;2个步骤,账户A减去500元,账户B加上500元:A减500èB加500;过程中如果插入个账户B取走500元:A减500èB减500èB加500。原本是2步,变为3步。转账结果就会发生错误。

只有简单的读取,储存操作才是原子操作如I= 10(变量之间的互相赋值不是原子操作),如果要实现更大范围操作的原子性,可以通过synchronized,因为锁能保证一个时刻只有一个线程执行代码块,从而不存在原子性问题。

        *可见性:当多个线程访问同一个变量,一个线程修改了变量的值,他线程能立即看到修改的值。

理解场景:CPU1下线程1执行int I = 0 , I = 10,CPU2下线程2执行 j = I ;假若线程1初始化  I = 0 后将 I = 10放入高速缓存中,但是没有放入主存中,此时线程2执行,从主存中读取的仍然是      I = 0 ,j 就不等于10。线程1已经改变了i的值,但是线程2没有立即看到线程1修改的值,导致可见性问题。

Synchronized的锁能保证同一时间只有一个线程执行代码块的同时,在释放锁之前会将对变量的修改刷新到主存当中,因此避免上面场景中的问题,进而解决可见性问题。

        *有序性:程序执行的顺序按照代码的先后顺序执行。

注意场景:指令重排序(InstructionReorder):处理器为了提高程序运行效率,可能会对输入的代码进行优化,不保证各个语句的执行先后顺序同代码顺序一致,但是保证执行结果一致(单线程一致,多线程233)。

Synchronized的锁能保证同一时间只有一个线程执行代码块,相当于单线程执行,自然保证有序性。

Volatile禁止进行指令重排序。

          5.3.使用volatile必须具备以下2个条件:(2个条件就是保证操作是原子性操作,从而保证使用volatile关键字的程序在并发时能够正确执行。)

*对变量的写操作不依赖于当前值

*该变量没有包含在具有其他变量的不变式中

          5.4.synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。

下周不是线程就是工厂模式。。


对于生活理想,应该像宗教徒对待宗教一样充满虔诚与热情!

你可能感兴趣的:(Android 设计模式—单例模式记要)