是ios 单例什么时候销毁的,开发的时候会不会影响性能

他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)你所不知道的单例模式和多线程并发在单例模式中的影响
单例对象(Singleton)是一种常用的设计模式。在应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
首先我们写一个简单的单例类:
public class Singleton {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static Singleton instance =
/* 私有构造方法,防止被实例化 */
private Singleton() {
/* 静态工程方法,创建实例 */
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对getInstance方法加synchronized关键字,如下:
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
但是,synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进。我们改成下面这个:
public static Singleton getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的
Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:
a&A、B线程同时进入了第一个if判断
b&A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
c&由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d&B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
e&此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
可能到了这里有些朋友可能会问为什么这样会出错???,下面我会详细的解释,这些都涉及到java中对class文件的处理,虚拟机对java指令的操作。
出现的问题也就是说instance = new Singleton();语句是分两步执行的。
不太明白为啥初始化那一步不执行完就跳出synchronized块??
这就是我开始看到这个代码问的一个问题。
本题涉及几个知识点。我详细解释下。
1.如果你是想在JAVA代码级别解释这个问题,那么你是在浪费时间。这个问题必须到JVM生成的代码级别讨论(很多问题都是这个样子,在JAVA代码级别讨论不仅浪费时间,而且没有意义,记得有人跟我说过一句话:在你所处理的层面,问题根本还没有浮现(非问题))。 ------引用自一位大神的解释
public class TestJVM {
public static void main(String[] args)
TestJVM abc = new TestJVM();
这个代码用 javap -c 命令反编译生成下面命令行:
public TestJVM();
invokespecial
#8; //Method java/lang/Object.&&:()V
public static void main(java.lang.String[]);
new #1; //class TestJVM
invokespecial
#16; //Method &&:()V
下面是一位大神写的解答:
解释这段代码是这道问题的第一步,建议你大概查阅下JVM规范。
(1) new 的含义是创造一块内存,并且在堆栈上压入指向这块内存的引用。
(2) dup的含义是将栈顶复制,并压入栈。(所以现在有了两个指向刚才分配内存的引用)
(3) invokespecial意思是将分配的内存中初始化对象。
(4) astore_1是将栈顶压入本地变量。
(这段过程,我建议你自己多画几遍,体会下JVM&面向堆栈&的概念,JVM规范第一章最好看看)
3.上面的四个步骤(绝对的物理过程),其实就是三件事(体会一下原子语句的含义):
a.给实例分配内存。
b.初始化构造器
c.将引用指向分配的内存空间(注意到这步引用就非null了)。
一般来说,我们期望执行的步骤是a-&b-&c,然而,由于JVM乱序执行的特性(自己查查这句话在哪,别轻易相信别人,虽然有时候文档也是会骗人的-!-),可能执行的顺序是a-&c-&b。当a-&c-&b这样执行时候,假如刚执行完c,这样线程2访问这个引用,发现引用不为空,他就对相应的内存做操作,这样就会发生错误,这种错误想必不容易发现(那是不是不容易发生?取决于具体的应用环境。)。
4.问题的关键用一句话来概括,就是这个意思:if(instance==null),如果instance !=null,那么instance就真的准备好了么?
所以,最原始的写法虽然慢,但是不会产生这种问题,因为原始写法把判断是否等于null的语句,也给锁起来了。只有得到锁,才有资格判断
5.上面的几条,你也许看了第四条,或者大概明白前几条,你的问题就能解答了。不精确的了解似乎也能回答,但是,有好多误解就产生了。
比如,有人说,加了valatile类型修饰(JVM1.5以后)符可以将LZ的写法变对,如private volatile static Singleton instance =
其实这是不对的,valatile(LZ想想为什么valatile影响效率?理解下寄存器和内存的效率差别)无非说的就是线程是不能保留共享对象的本地
拷贝(正常情况线程是可以保留的),那是不是每次去内存中取,就能保证单例对象的正常初始化呢?很明显,这完全是两个问题。
6.很多细节问题(编程方面),你都得查查英文文档,得自己写试试,中文大家说的话都非常像(因为都是同一本书里面说的,再加上第一个
人的翻译水平不咋样),很多误解就此产生。
另外一位大神的总结:
1.java寄存器读写是无序的,这也是问题的根源。
2.针对这个问题就是在于最外层的if语句不在同步块中,所以即使下面的同步块是正确的,且同步块具有可线性化特性,但是这些都是语言级的功能,换句话说,是java来保证这些同步操作是一个原子操作。所以没在同步块中的if有可能拿到一个没有初始化完全的对象。
3.volatile 只是具备同步刷新寄存器于缓存之间的功能,这个步骤是原子操作。以上面为例,cache = new ConcurrentHashMap(); 这个操作不保证ConcurrentHashMap初始化以完成。但保证指针指向的分配区域所有线程可见。
所以这句话写成
Map temp = new ConcurrentHashMap();
同样存在问题。
这里是根据现在的解答总结的原因:
我们以A、B两个线程为例:
a&A、B线程同时进入了第一个if判断
b&A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
c&由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d&B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
e&此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
个人见解:在jvm中,jvm通过.class文件得来的java指令,这些指令在每一个方法中都相当于一个指令集合,每次运行这个方法都会运行这个指令集合。然后对于前面synchronized中的问题代码:
public static Singleton getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
其中这个代码:
synchronized (instance) {
if (instance == null) {
instance = new Singleton();
我猜测这里生成的几个指令在这里乱序执行,其实前面那位大神也说了是乱序执行。所以当出了synchronized后,还有构造方法未初始化的说法。
通过return指令再结束这个指令集,也就是方法。所以说,这也就能解释下面的下面(指的是,下面这个单例模式的下面一个单例模式例子)的一个单例模式实例能行,而上面这个单例模式实例不行了。
所以程序还是有可能发生错误,其实程序在运行过程是很复杂的,从这点我们就可以看出,尤其是在写多线程环境下的程序更有难度,有挑战性。我们对该程序做进一步优化:
private static class SingletonFactory{
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return SingletonFactory.
实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:
public class Singleton {
/* 私有构造方法,防止被实例化 */
private Singleton() {
/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
/* 获取实例 */
public static Singleton getInstance() {
return SingletonFactory.
/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
public Object readResolve() {
return getInstance();
其实说它完美,也不一定,如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。所以说,十分完美的东西是没有的,我们只能根据实际情况,选择最适合自己应用场景的实现方法。也有人这样实现:因为我们只需要在创建类的时候进行同步,所以只要将创建和getInstance()分开,单独为创建加synchronized关键字,也是可以的:
public class SingletonTest {
private static SingletonTest instance =
private SingletonTest() {
private static synchronized void syncInit() {
if (instance == null) {
instance = new SingletonTest();
public static SingletonTest getInstance() {
if (instance == null) {
syncInit();
考虑性能的话,整个程序只需创建一次实例,所以性能也不会有什么影响。
补充:下面再来一个&影子模式&:
采用&影子实例&的办法具体说,就是在更新属性时,直接生成另一个单例对象实例,这个新生成的单例对象实例将从或文件中读取最新的配置信息;然后将这些配置信息直接赋值给旧单例对象的属性。
public class GlobalConfig {
private static GlobalConfig instance =
private Vector properties =
private GlobalConfig() {
//Load configuration information from DB or file
//Set values for properties
private static synchronized void syncInit() {
if (instance = null) {
instance = new GlobalConfig();
public static GlobalConfig getInstance() {
if (instance = null) {
syncInit();
public Vector getProperties() {
public void updateProperties() {
//Load updated configuration information by new a GlobalConfig object
GlobalConfig shadow = new GlobalConfig();
properties = shadow.getProperties();
注意:在更新方法中,通过生成新的GlobalConfig的实例,从文件或数据库中得到最新配置信息,并存放到properties属性中。上面两个方法比较起来,第二个方法更好,首先,编程更简单;其次,没有那么多的同步操作,对性能的影响也不大。
通过单例模式的学习告诉我们:
1、单例模式理解起来简单,但是具体实现起来还是有一定的难度。
2、synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)。
到这儿,单例模式基本已经讲完了,结尾处,笔者突然想到另一个问题,就是采用类的静态方法,实现单例模式的效果,也是可行的,此处二者有什么不同?
首先,静态类不能实现接口。(从类的角度说是可以的,但是那样就破坏了静态了。因为接口中不允许有static修饰的方法,所以即使实现了也是非静态的)
其次,单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。
再次,单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。
最后一点,单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,但是静态类不行。从上面这些概括中,基本可以看出二者的区别,但是,从另一方面讲,我们上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有很大的关联,只是我们考虑问题的层面不同罢了。两种思想的结合,才能造就出完美的解决方案,就像HashMap采用数组+链表来实现一样,其实生活中很多事情都是这样,单用不同的方法来处理问题,总是有优点也有缺点,最完美的方法是,结合各个方法的优点,才能最好的解决问题!单例模式的好处和缺点?为什么要用单例模式?详细才给分_百度知道
单例模式的好处和缺点?为什么要用单例模式?详细才给分
单例模式的好处和缺点?为什么要用单例模式?
我有更好的答案
【单例模式含义】单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。【采用单例模式动机、原因】对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。【单例模式优缺点】【优点】一、实例控制单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。二、灵活性因为类控制了实例化过程,所以类可以灵活更改实例化过程。【缺点】一、开销虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。二、可能的开发混淆使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。三、对象生存期不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。
因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等2 单例的缺点 就是不适用于变化的对象,因此节省内存,加快对象访问速度1 单例模式 只允许创建一个对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。用单例模式,就是在适用其优点的状态下使用
为您推荐:
其他类似问题
您可能关注的内容
缺点的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。开发中写单例的正确姿势 - 简书
开发中写单例的正确姿势
public class SingletonClass {
private static volatile SingletonClass sSoleI
//private constructor.
private SingletonClass() {
//Prevent form the reflection api.
if (sSoleInstance != null) {
throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
public static SingletonClass getInstance() {
//Double check locking pattern
if (sSoleInstance == null) { //Check for the first time
synchronized (SingletonClass.class) {
//Check for the second time.
// if there is no instance available... create new one
if (sSoleInstance == null)
sSoleInstance = new SingletonClass();
return sSoleI
单例模式使用中常见错误:
1.getInstance()不要加锁,
【1】锁开销会影响性能
【2】如果sSoleInstance已经初始化了,锁机制就不需要了。
2.volatile:可以保证写入操作在读取操作之前发生。
public class SingletonClass implements Serializable {
private static volatile SingletonClass sSoleI
//private constructor.
private SingletonClass() {
//Prevent form the reflection api.保证该私有构造函数只会执行一次,即第一次:sSoleInstance为null的时候。
if (sSoleInstance != null) {
throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
public static SingletonClass getInstance() {
if (sSoleInstance == null) {
//if there is no instance available... create new one
synchronized (SingletonClass.class) {
if (sSoleInstance == null)
sSoleInstance = new SingletonClass();
return sSoleI
//Make singleton from serialize and deserialize operation.
protected SingletonClass readResolve() {
return getInstance();
上面写法可以保证:该类事线程,反射,序列化都是安全的。
再谈谈为什么使用单例:
1.控制对象的创建,限制对象的数量只有一个。
2.单例允许只有一个入口来创建对象。
3.常用在控制资源,比如:数据库连接等等。
4.单例的写法比较多,根据不同的情况进行不同的写法,基本围绕上面给出的写法。
前言 本文大部分文字来自于微信公众号:腾讯Bugly 里的一篇文章——那些年,我们一起写过的 “单例模式”(http://mp.weixin.qq.com/s/wEK3UcHjaHz1x-iXoW4_VQ ),强烈建议点击链接直接阅读原文。 何为单例模式? 顾名思义,单例模...
百战程序员_ Java1573题 QQ群:034603 掌握80%年薪20万掌握50%年薪10万 全程项目穿插, 从易到难,含17个项目视频和资料持续更新,请关注www.itbaizhan.com 国内最牛七星级团队马士兵、高淇等11位十年开发经验专...
文章转载自「开发者圆桌」一个关于开发者入门、进阶、踩坑的微信公众号 单例模式可能是大家经常接触和使用的一个设计模式,你可能会这么写: public class Test { private static T private Test() { } p...
1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io的语法,虚拟机方面的语法。 1、一个&.java&源文件中是否可以包括多个类(不是内部类)?有什么限制? 可以有多个类,但只能有一个publ...
1 场景问题# 1.1 读取配置文件的内容## 考虑这样一个应用,读取配置文件的内容。 很多应用项目,都有与应用相关的配置文件,这些配置文件多是由项目开发人员自定义的,在里面定义一些应用需要的参数数据。当然在实际的项目中,这种配置文件多采用xml格式的。也有采用proper...
怎样离开渣男男友。自己实力够强,请立马变得有钱。把事业操持好。 累的像条狗还要被说成这有什么累的,这样的男友有了比没有更加难受。不体贴,小气,从来不会给你买礼物,买个啥小东西都会嘚瑟得不得了。从来只会指责我。受够了。创业办工作室,只帮忙拼贴了下厕所地板,还有前台桌子。算什么...
这是第一次制作思维导图,欢迎提出指导建议,另外有个小小得问题,我在创建项目得时候创建子项目快捷键是D,但是我按了D没有反应,ctrl shift tab alt +D都不不得行,谁来教教我………… 这是领英出得一本职场励志方向得书,书名一听就特别鸡血,我就是奔着这个书名才买...
Sayings 最近迷上了一个动画导演:今敏。 他最擅长的,是将虚幻之境(梦、电影、幻想等等)与现实糅合,营造一种光怪陆离,纷繁迷人的影像世界。 私以为,今敏先生在宫崎骏、迪斯尼等主流的动画方向之外,开辟出了一片独特而迷人的领域。 因此他被称为「造梦大师」。 今天推介的这部...
红楼梦一书中诗词众多,但我觉得其中涵义最深的,还要数好了歌。 好了歌十分简单,读起来也通俗易懂。整首好了歌,却集中反映了,封建社会的贵族们,对于功名,金钱,美色,儿女,费尽心机的苦苦追求。 但在我看来,曹雪芹虽然通过这首好了歌,对于这种现象的残酷和虚伪给予了无情的评击,但是...
我是80后,没有任何背景的80后,出生在广阔的农村。 没有深墙大院的深思熟虑,没有书香世家的大方稳重,就是一只乳臭未干的小牛犊子,不怕天高地厚,只相信海阔天空。 只有满身满身的乡土气息,而这是我最爱的骨子里的味道。 小的时候城里人笑话我是乡下人,现在长大了成了回不去的城里人...

我要回帖

更多关于 java 什么时候用单例 的文章

 

随机推荐