单例与多线程

一。饿汉模式

public class Singleton{         

 private static Singleton instance = new Singleton();         

    private Singleton(){         

     }         

    private static Singleton getInstance(){
        return instance;
     }
} 

  第一次加载类时就会创建Singleton 实例,所以是线程安全的。另一方面,如果这个Singleton 实例的创建非常消耗系统资源,

而应用始终都没有使用Singleton 实例,那么创建Singleton 消耗的系统资源就被白白浪费了。

二。饱汉模式

public class Singleton{
     private static Singleton instance = null;
     private Singleton(){

     }
     public static Singleton getInstance(){
            if (instance == null){
                 instance = new Singleton();
            }
           return instance;
    }
}

  线程不安全:两个线程A 和B 同时进入该方法的情形
1. A 进入if 判断,此时foo 为null,因此进入if 语句
2. B 进入if 判断,此时A 还没有创建foo,因此foo 也为null,因此B 也进入if 语句
3. A 创建了一个Foo 并返回
4. B 也创建了一个Foo 并返回

三。解决方法

1.同步方法

 public static synchronized Singleton getInstance(){
         if (instance == null){
                instance = new Singleton();
          }
         return instance;
 }

  这种解决办法的确可以防止错误的出现,但是它却很影响性能:每次调用getInstance 方法的时候都必须获得
Singleton 的锁,而实际上,当单例实例被创建以后,其后的请求没有必要再使用互斥机制了

2.同步块


   public static Singleton getInstance(){
     if(single == null){
          synchronized (Singleton.class) {    //保证了同一时间只能只能有一个对象访问此同步块
              if(single == null){
                  single = new Singleton();
              }
          }
     }
     return single;
   }   

  上述描述似乎已经解决了我们面临的所有问题,但从JVM 的角度讲,这些代码仍然可能发生错误。对于JVM 而言,它执行的是一个个Java 指令。
在Java 指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton()语句是分两步执行的。但是JVM 并不保证这两个操作的
先后顺序,也就是说有可能JVM 会为新的Singleton 实例分配空间,然后直接赋值给instance 成员,然后再去初始化这个Singleton 实例。这样就
可能出错,我们仍然以A、B 两个线程为例:
1. A、B 线程同时进入了第一个if 判断
2. A 首先进入synchronized 块,由于instance 为null,所以它执行instance = new Singleton();
3. 由于JVM 内部的优化机制,JVM 先画出了一些分配给Singleton 实例的空白内存,并赋值给instance成员(注意此时JVM 没有开始初始化这个实例)
然后A 离开了synchronized 块。
4. B 进入synchronized 块,由于instance 此时不是null,因此它马上离开了synchronized 块并将结果返回给调用该方法的程序。
5. 此时B 线程打算使用Singleton 实例,却发现它没有被初始化,于是错误发生了。

3.内部类

public class Singleton {

        private Singleton(){        

     }        

    private class SingletonHoledr(){
        private static Singleton instance = new Singleton();
     }        

    private static Singleton getInstance(){
        return SingletonHoledr.instance;
     }
} 

单例与多线程

时间: 2024-10-15 14:02:52

单例与多线程的相关文章

线程学习--(六)单例和多线程、ThreadLocal

一.ThreadLocal 使用wait/notify方式实现的线程安全,性能将受到很大影响.解决方案是用空间换时间,不用锁也能实现线程安全. 来看一个小例子,在线程内的set.get就是threadLocal package thread2; public class ConnThreadLocal { public static ThreadLocal<String> th = new ThreadLocal<String>(); public void setTh(Strin

架构师养成记--6.单例和多线程、ThreadLocal

一.ThreadLocal 使用wait/notify方式实现的线程安全,性能将受到很大影响.解决方案是用空间换时间,不用锁也能实现线程安全. 来看一个小例子,在线程内的set.get就是threadLocal 1 public class ConnThreadLocal { 2 3 public static ThreadLocal<String> th = new ThreadLocal<String>(); 4 5 public void setTh(String value

java懒汉式单例遇到多线程

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或少具有资源管理器的功能. 每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中.每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用.总之,选择单例模式就是为了避免不一致状态,避免政出多头. 1 public cl

java单例 同步 多线程

get="_blank" href="http://www.csdn.net/tag/%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f">设计模式多线程javaclassthreadstring 单例模式的特点: 只创建一次 私有的属性 公有的访问方法 单例模式的分类: 懒汉(LazySingleton):默认不自动实例化,等到用的时候根据当前情况实例化,并且返回当前对象: 恶汉(EagerSingleton):在类第一次加载的时候强制

单例在多线程中的使用

一次执行  dispatch_once: 对应的代码只执行一次 , 并且它是线程安全的, 系统会自动这个函数加锁,保存同一时间只有一个线程去执行任务, 实现真正意义的一次性执行 什么时候需要用到一次性执行:  单例 单例: 就是在程序运行期间,只有一个实例化对象  ---- 举例:音乐播放器的App, 同时只能播放一首歌曲. 好处: 如果不使用同一个播放管理器对象, 那就播放下一首歌曲时, 先销毁上一个对象, 再创建一个新对象 这样做就比较消耗资源. 既然同一个时刻只能播放一首歌曲 ,所以我们只

单例类多线程

作为设计模式理论中的Helloworld,相信学习java语言的人,都应该听说过单例模式.单例模式作为对象的一种创建模式,它的作用是确保某一个类在整个系统中只有一个实例,而且自行实例化并向整个系统提供这个实例. 由此可见,单例模式具有以下的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一的实例. 单例类必须给所有其他对象提供这一实例. 由于Java语言的特点,使得单例模式在Java语言的实现上有自己的特点.这些特点主要表现在单例类如何将自己实例化. 饿汉式单例类 饿汉式单例类是在Ja

单例和多线程

要保证在多线程环境下的单例模式,有下面两种建议的方式: 一.静态内部类 public class Singletion { private static class InnerSingletion { private static Singletion single = new Singletion(); } public static Singletion getInstance(){ return InnerSingletion.single; } }  二.double check的方式

线程学习--(七)单例和多线程、同步类容器和并发类容器

一.同步类容器 同步类容器都是线程安全的,但在某些场景下可能需要加锁来保护复合操作.复合类操作如:迭代(反复访问元素,遍历完容器中的所有元素).跳转(根据指定的顺序找到当前元素的下一个元素).以及条件运算.这些复合操作在多线程并发的修改容器时,可能会表现出意外的行为,最经典的便是ConcurrentModificationException,原因是当容器迭代的过程中,被并发的修改了内容,这是由于早期迭代器设计的时候并没有考虑并发修改的问题. 同步类容器:如古老的Vector/HashTable.

并发编程(8):单例与多线程

单例模式,最常见的就是饥饿模式和懒汉模式,一个直接实例化对象,一个在调用方法时进行实例化对象.在多线程模式中,考虑到性能和线程安全问题,我们一般选择下面两种比较经典的单例模式,在性能提高的同时,又保证了线程安全. dubble check instance static inner class 静态内部类static inner class public class InnerSingleton { private static class Singletion { private static