单例模式在多线程下的问题

首先一个简单的单例类:

public class Logger {
    private static Logger log = null;
    // 构造函数私有化
    private Logger() {
    }
    public static Logger getLogger() {
        if (log == null) {
            log = new Logger();
        }
        return log;
    }
}  

该类当放入多线程的环境中,肯定 就会出现问题,如何解决?

   1,第一种方式:在方法getLogger上加上synchronized关键字:

public static synchronized Logger getLogger(){
    if(log == null){
        log = new Logger();
    }
    return log;
}    

    缺点:synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降。

原因:每次调用getInstance(),都要对对象上锁。

  2,第二种方式:synchronized关键字锁住if方法:

public static Logger getLogger(){
    synchronized (log) {
        if(log == null){
            log = new Logger();
        }
    }
    return log;
}

  该方式的问题:

在Java指令中创建对象和赋值操作是分开进行的,也就是说log = new Logger();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Logger实例分配空间,然后直接赋值给log成员,然后再去初始化这个Logger实例。这样就可能出错。

以A、B两个线程为例:

      1,A、B线程同时进入了第一个if判断

      2,A首先进入synchronized块,由于log 为null,所以它执行log = new Logger();

      3,由于JVM内部的优化机制,JVM先画出了一些分配给Logger实例的空白内存,并赋值给log 成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。

      4,B进入synchronized块,由于log 此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。

      5,此时B线程打算使用Logger实例,却发现它没有被初始化,于是错误发生了。

   解决该问题:

    方案1:创建一个内部类:

public class Logger {
    private static Logger log = null;
    //构造函数私有化
    private Logger(){}
    //创建一个私有的静态内部类
    private static class LoggerFactory{
        private static Logger logger = new Logger();
    }
    public static Logger getLogger(){
        return LoggerFactory.logger;
    }
}    

     方案2:把创建对象和获取对象分开:

public class Logger2 {
    private static Logger2 log = null;
    //构造函数私有化
    private Logger2(){}
    public synchronized void setLogger(){
        if(log == null){
            log = new Logger2();
        }
    }
    public Logger2 getLogger(){
        if(log == null){
            setLogger();//初始化log
        }
        return log;
    }
}    

     方案3:采用“影子实例”同步单例对象属性的同步跟新:

public class Logger {
    private static Logger log = null;
    private Vector properties = null;

    public Vector getProperties() {
        return properties;
    }

    public static Logger getLogger(){
        if(log == null){
            syncInit();
        }
        return log;
    }

    public static synchronized void syncInit(){//log对象初始化
        if(log == null){
            log = new Logger();
        }
    }

    // 替换掉原有log里面的影子properties
    public void updatePropertis() {
        Logger log1 = new Logger();
        properties = log1.getProperties();
    }

    // 构造函数私有化
    private Logger() {
        //这里模拟从服务器里面读取配置信息,赋值给properties改对象
    }
}

采用类的静态方法,实现单例模式的效果和不使用静态方法实现的单例模式的区别:

1,静态类不能实现接口。(从类的角度说是可以的,但是那样就破坏了静态了。因为接口中不允许有static修饰的方法,所以即使实现了也是非静态的)

2,单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。

3,单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。

参考质料:http://www.ibm.com/developerworks/cn/java/l-singleton/

时间: 2024-10-20 05:05:32

单例模式在多线程下的问题的相关文章

4创建型模式之单例模式__多线程下的懒汉式单例和饿汉式单例

//1"懒汉"模式虽然有优点,但是每次调用GetInstance()静态方法时,必须判断 //      NULL == m_instance,使程序相对开销增大. //2多线程中会导致多个实例的产生,从而导致运行代码不正确以及内存的泄露. //3提供释放资源的函数 讨论:   这是因为C++中构造函数并不是线程安全的. C++中的构造函数简单来说分两步: 第一步:内存分配 第二步:初始化成员变量 由于多线程的关系,可能当我们在分配内存好了以后,还没来得急初始化成员变量,就进行线程切换

单例模式在多线程下的多种实现模式

单例模式是23种设计模式中比较常见的设计模式,又因为其代码量精简,所以经常会被用在在面试中测试面试者的能力. 初级的单例模式很简单 实现两个要求 1构造方法私有化 2对外提供静态的,公开的获取对象的方法 所以:初级单例模式如下 public class Singelton {private Singelton(){} private static Singelton sin=null;public static Singelton getSingelton(){           if(sin

java单例模式,多线程下实现

单例模式 必备条件: 1:private的构造方法. 2:private static 对象保存该类实例. 3:static方法返回该类实例. (一)饿汉模式 /** * 单例模式 * 1:线程安全实现 * 2:浪费内存 * @author 祥少 * */public class SingletonTest { //final关键字,导致一旦被初始化,一直占用该段内存(即使你不使用)    public static final SingletonTest singletonTest = new

多线程下的单例模式

参加一个面试,被问到多线程下的单例模式会创建几个对象,总结一下: 首先我的单例是这么写的(懒汉式) public class Singleton{ private static Singleton singleton; private Singleton(){} public Singleton getInstance(){ if(singleton == null){ singleton = new singleton(); } return singleton; } } 这样写的话, 当线程

java工程优化——多线程下的单例模式

在最初学习设计模式时,我为绝佳的设计思想激动不已,在以后的工程中,多次融合设计模式,而在当下的设计中,我们已经觉察出了当初设计模式的高瞻远瞩,但是也有一些不足,需要我们去改进,有人说过,世界上没有绝对的事,当然,再简单的事情,环境变了,也会发生变化,今天和大家一起分享在多线程下单例模式的优化. 1,传统 首先,我们回顾下传统的单例(懒汉式)是如何工作的: public class SingletonClass{ private static SingletonClass instance=nul

单例模式在多线程环境下的lazy模式为什么要加两个if(instance==null)

刚才在看阿寻的博客”C#设计模式学习笔记-单例模式“时,发现了评论里有几个人在问单例模式在多线程环境下为什么lazy模式要加两个if进行判断,评论中的一个哥们剑过不留痕,给他们写了一个demo来告诉他们为什么. 我看了一下这个demo,确实说明了这个问题,但我认为不够直观,呵呵,于是我就稍微的改了一下. 这是剑过不留痕的demo using System; using System.Threading; namespace SingletonPattern { class Program { s

单例模式与多线程

概述 关于一般单例模式的创建和分析在我的另一篇博客<Java设计模式--单件模式>中有详细说明.只是在上篇博客中的单例是针对于单线程的操作,而对于多线程却并不适用,本文就从单例模式与多线程安全的角度出发,讲解单例模式在多线程中应该如何被使用. 版权说明 著作权归作者所有. 商业转载请联系作者获得授权,非商业转载请注明出处. 本文作者:Coding-Naga 发表日期: 2016年4月6日 本文链接:http://blog.csdn.net/lemon_tree12138/article/det

6 单例模式及其多线程问题

一.单例模式 单例模式可以保证一个类仅有一个实例,这个模式应该更简单工厂一样常用了吧,但对我来说,以前都是瞎用,这是第一次深度学习单例模式. 最简单的单例模式代码是这样的: class Singleton { private static Singleton _instance; private Singleton() { } public static Singleton GetInstance() { if (_instance == null) _instance = new Single

Java - 单例模式与多线程

单例模式大家并不陌生,分为饿汉式和懒汉式等. 线程安全的饿汉式单例 饿汉式单例在类第一次加载的时候就完成了初始化,上代码: public class MyObject { private static MyObject myObject = new MyObject(); public static MyObject getInstance(){ return myObject; } } 下面来验证饿汉式单例的线程安全性: public class MyThread extends Thread