先来看这样一个单例,稍微有点经验的同学可能都会说,这样的单例是非线程安全的。要加个volatile关键字才可以。class Singleton{        private static  Singleton singleton;        private Singleton(){};        public static Singleton getInstance()

{            if (singleton==null)

{                synchronized (Singleton.class)

{                    if (singleton==null)

{

singleton=new Singleton();

}

}

}            return singleton;

}

}

复制代码但是你要是问他,为什么是非线程安全的单例就答不出来了。搞清楚这个问题其实 对我们的多线程理解是很有好处的。

我们首先明确一下对于jvm来说,完成对一个变量的写操作 到底是如何进行的。

写操作:

(1)先把值写入cpu的高速缓存cache中。(2)然后再把这个cache中的值拷贝到ram(也就是我们的内存)中。

注意啊,对于一个写操作来说,这个(1)(2) 可不是原子操作,很有可能(1)执行完毕以后,cpu又去干了其他事情,

并没有第一时间把cache的值 写入到ram中。而我们读操作,都是从ram中去读取一个值的。

所以这里我们可以想一下,如果是多线程场景的话,会有一些坑。

然后再说一个概念,对于 singleton=new Singleton(); 这一条语句来说,他显然不是一条指令就可以完成的。

正常情况来说,我们要完成这条语句涉及到的指令大约如下:

1.申请一段堆内存空间

2.在这个堆内存空间中把我们需要的对象初始化完毕

3.把singleton这个引用指向我们的堆内存空间地址。

但是坑爹就坑爹在,虚拟机会有一个指令重排序的概念。当虚拟机发现单线程下 指令的顺序变更不会导致结果异常的时候

就会触发指令重排序的机制, 他会导致上述的 123顺序发生变更,比如我们把顺序改成132 你就会发现 结果还是一样的。

(指令重排序的触发机制准确的来说是happens before原则 有兴趣的同学可以深挖)

如果发生132的执行顺序 会发生什么?

假设线程a 进入到了同步代码块中,这个时候触发了指令重排序,顺序变成132,假设cpu这个时候执行了13。然后转头

去执行线程b,线程b 进入getInstance方法的时候,他发现singleton 不是null了,于是欢天喜地的return了,

但是要知道这个时候线程a的 2还没执行,也就是说singleton虽然不是空,但是他指向的地址空间里面啥都没有,对象还没有初始化。所以这是一个非常大的隐患,虽然他发生的概率极低,低到我现在都没有复现过这种现象,但是依旧有概率。

那么正确的写法:class Singleton{     private static volatile Singleton singleton;     private Singleton(){};     public static Singleton getInstance()

{         if (singleton==null)

{             synchronized (Singleton.class)

{                 if (singleton==null)

{

singleton=new Singleton();

}

}

}         return singleton;

}

}

复制代码有很多人就会说 volatile 这个关键字以后,singleton=new Singleton(); 就不会发生指令重排了,所以这么做是正确的。

现在明确的告诉你,上面这个观点是错误的

singleton=new Singleton();  这条语句背后的指令依旧有概率发生指令重排,只不过 volatile修饰过以后,在 这条语句背后的

指令完全执行完毕以前,对singleton这个引用的读操作全部被屏蔽了。

也就是说 132的执行顺序依旧会发生,只不过 当执行完13 而2没有执行的时候,volatile修饰过的这个变量,所有对他的读操作

都会暂时屏蔽,等待2操作执行完以后,才会进行读操作。

这才是volatile关键字加上去以后的作用。

android很多代码比如eventbus的单例就是用的上述写法。

当然了,上述写法是典型的懒汉写法,所谓懒汉你就理解成用的时候才实例化,不用的话不实例化。

但是如果你的需求是这个单例无论在什么情况下都会存在,你当然可以写成饿汉,饿汉的写法更简单。

缺点就是他会一直占用内存。饿汉写法很多,我写个最简单的:class Singleton {     //最简单的写法就是这个了,直接public就行

public static final Singleton instance = new Singleton();     private Singleton() {

}

}

复制代码单例序列化会破坏对象唯一性吗?

答案是会的:package com.wuyue.test;import java.io.*;/**

* Created by 16040657 on 2019/2/12.

*/public class Test2 {    public static void main(String args[]) {

Singleton s1 = Singleton.instance;

File f = new File("../test.txt");        try {

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));

oos.writeObject(s1);

oos.close();

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f));

Singleton s3 = (Singleton) ois.readObject();

System.out.println("s1==s3:" + (s1 == s3));

} catch (IOException e) {

e.printStackTrace();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}    static class Singleton implements Serializable {        //最简单的写法就是这个了,直接public就行

public static final Singleton instance = new Singleton();        private Singleton() {

}//        //这个方法就可以保证序列化和反序列化得到的对象是同一个了//        private Object readResolve() {//            return instance;//        }

}

}

复制代码代码比较简单,大家可以测试一下,s1和s3就是2个不同的对象,但是如果把注释掉的readResolve方法放开的话,你就会发现

这个问题解决了,序列化和反序列化是同一个对象了。

对外部公开提供的sdk的单例要注意些什么?

尤其是对于很多金融安全类的sdk来说,如果你这个里面有单例的话,涉及到安全性要尽可能的不被业务方hook,

其中尤其要注意的就是 有人可能会利用反射来new一个对象,破坏单例

解决这个问题也不难,private Singleton() {            //防止有人利用反射恶意修改

if (null != instance) {                throw new RuntimeException("dont construct more!");

}

}

复制代码项目中的单例太多,如何有效管理?

其实就拿map管理就可以了,android里面的 wms,ams 等等系统单例服务都是这样的。你传一个key进去 返回一个单例给你。

这个真的很有用哦,特别是大型工程,可以有效管理单例,文档输出就简单许多。static class SingletonManager {

private static Map objectMap = new HashMap<>();     private SingletonManager() {

}     public static void registerService(String key, Object ins) {         if (!objectMap.containsKey(key)) {

objectMap.put(key, ins);

}

}     public static Object getService(String key) {         return objectMap.get(key);

}

}

复制代码android中使用单例还要注意些什么?

最主要的就是尽量不要利用单例模式存储传递数据,因为app挂在后台的时候进程会容易被杀掉,如果回到前台再取这个单例里的

数据很容易就取到个null,所以android中写单例的原则就是:

原则上不允许用单例模式传递数据,如果一定要这么做,请考虑数据恢复现场。

作者:Android进阶开发

链接:https://www.jianshu.com/p/4c05aa06545d

android 单例的作用,Android中单例模式的几个坑相关推荐

  1. java中的单例_细说Java中的几种单例模式

    在Java中,单例模式分为很多种,本人所了解的单例模式有以下几种,如有不全还请大家留言指点: 饿汉式 懒汉式/Double check(双重检索) 静态内部类 枚举单例 一.饿汉式 image 饿汉式 ...

  2. android 单例销毁,【设计模式与Android】单例模式——独一无二的皇帝

    什么是单例模式 所谓单例模式,就是确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例的设计模式.单例模式是最简单的设计模式,也是应用最广的设计模式.一般用于避免产生多个对象消耗过多的资源 ...

  3. android 单例存储,Android 单例在内存中存储数据

    开发中我们有时候需要在APP运行中存储一些数据,存储在内存中,这时候创建一个单例就非常方便,我只做为笔记发布,大家随意看看 public class Singleton { //单例模式实例 priv ...

  4. android任务栈的作用,Android 启动模式以及任务栈

    1.Android 主要包含4种启动模式,下面分别介绍四种启动方式的特点: 1)tandard 默认的启动模式,标准模式 每开启一个Activity,就会在栈顶添加一个Activity实例.多次间隔或 ...

  5. 单例销毁_TypeScript 设计模式之单例模式

    一.简介 单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池.全局缓存.浏览器中的 window 对象等.单例模式用于保证一个类仅有一个实例,并提供一个访问它的全局访问点. 二.优缺点 ...

  6. java设计模式 单例_java设计模式一(单例模式singleton)

    1 概述 如果要保证系统里一个类最多只能存在一个实例时,我们就需要单例模式.这种情况在我们应用中经常碰到,例如缓存池.数据库连接池.线程池.一些应用服务实例等.在多线程环境中为了保证实例的唯一性其实并 ...

  7. java23种设计模式+单例_Java23种设计模式之单例模式

    一.单例模式简介 单例模式是Java设计模式中常见的一种模式.主要分为懒汉式单例.饿汉式单例.登记式单例: 单例模式的特点:  1.单例类只能有一个实例:  2.单例类必须自己创建自己的唯一的实例: ...

  8. java单例设计模式_Java设计模式之单例模式详解

    在Java开发过程中,很多场景下都会碰到或要用到单例模式,在设计模式里也是经常作为指导学习的热门模式之一,相信每位开发同事都用到过.我们总是沿着前辈的足迹去做设定好的思路,往往没去探究为何这么做,所以 ...

  9. java单例设计模式双重_Java 设计模式 ——单例模式(饿汉,懒汉,双重锁,静态内部类)...

    设计模式: 是在大量的实践中总结和理论化之后优选的代码结构,编程风格,以及解决问题的思考方式.设计模式免去我们自己再思考和摸索.就像是经典的棋谱,不同的棋局,我们用不同的棋谱 俗称"套路&q ...

最新文章

  1. UVA 10700 Camel trading
  2. WebBrowser控件判断完全加载中DocumentCompleted和Navigated的关系
  3. Java常用类之要点总结
  4. 基于OpenCV的简单人脸识别系统
  5. IOS开发--TextField
  6. cmd pc如何开多个微信_抖音打击刷赞刷粉,240多个百万粉丝大V被封;微信PC版再更新...
  7. (转)你的团队需要一个领袖,而不是一个主管
  8. RabbitMQ学习总结(1)——基础概念详细介绍
  9. web调试:ie缓存
  10. ubuntu 14.04 安装 diffmerge
  11. 解决无法从Git官网下载Git安装包的具体方法
  12. 投资热议:为什么另类数据对数字转型至关重要?
  13. Linux使用sendmail邮件监控[运维监控]
  14. 开关电源的开关管一般用MOS管而不是三极管原因
  15. 提取ansible hosts分组IP
  16. MyBatis入门级(增删改查)
  17. python对excel某一列求和-如何对某一列自动分组,统计求和
  18. Apache Camel中的recipientList和routingSlip的区别?
  19. Ubuntu 快速显示桌面快捷键
  20. hdfs - balancer学习

热门文章

  1. 28 | 案例篇:一个SQL查询要15秒,这是怎么回事?
  2. 部署zabbix企业监控平台
  3. dataframe第二列 r语言_R语言-数据框
  4. #1049 : 后序遍历(二叉树,DFS)
  5. 2021云栖大会丨首批阿里云计算巢认证合作伙伴获得授牌,阿里云与合作伙伴共筑云上生态
  6. 《斗罗大陆》引入阿里云云原生数据库 PolarDB 游戏体验更流畅
  7. 智能建筑进入新的十年
  8. 腾讯NExT Studios万字解读:我们是怎样将一款偏硬核的Roguelike游戏大众化的?
  9. 游戏中常用的寻路算法的分享(3):A*算法的实现
  10. wait_timeout【Mysql】解决方案