码迷,mamicode.com
首页 > 编程语言 > 详细

设计模式-单例模式(饿汉式及懒汉式的Java实现)

时间:2015-08-04 00:48:19      阅读:129      评论:0      收藏:0      [点我收藏+]

标签:java   单例模式   设计模式   

单例模式

单例模式在程序设计中使用的频率非常之高,其设计的目的是为了在程序中提供唯一一个对象(保证只被构造一次),例如写入日志的log对象,windows的任务管理器实现(只能打开一个)。这里主要介绍单例模式使用Java的实现(包括饿汉式及懒汉式)。

实现

这里使用Log类作为例子,Log对象需要在程序中只有一个对象且只初始化一次。

饿汉式

饿汉式的单例模式理解起来是比较容易的,就是在单例类加载的时候就初始化需要单例的对象。实现也比较容易。

public class Singleton{
    private static Log logObj = new Log();

    public static Log getInstance(){
        return logObj;
    }
}

懒汉式

如果logObj需要占用很大的内存,如果一开始就初始化logObj,那么会占用大量的内存。此时,有人就想,如果我在想用的时候再初始化Log类的对象,像懒汉一样,只有用到的时候再初始化,需要怎么设计呢?

实现一(非线程安全版本)


public class Singleton{
    private static Log logObj = null;

    public static Log getInstance(){
        if(logObj == null){
            logObj = new Log();
        }
        return logObj;
    }
}

实现二(线程安全版本)


public class Singleton{
    private static Log logObj = null;

    public static synchronized Log getInstance(){
        if(logObj == null){
            logObj = new Log();
        }
        return logObj;
    }
}

为了实现线程安全,这个版本的实现牺牲了一定的效率,如果logObj已经初始化,那么其他线程还需要同步的进入getInstance方法,会造成效率的损失。于是,有些人实现了下面的版本。

实现三(错误版本)

public class Singleton{
    private static Log logObj = null;

    public static Log getInstance(){
        if(logObj == null){
            synchronized(Singleton.class){
                if(logObj == null){
                    logObj = new Log();
                }
            }
        }
        return logObj;
    }
}

乍看起来上面的版本是没问题的,如果某个线程A发现logObj 还没初始化,那么就进入同步块初始化logObj,如果在这期间有其他线程B进入,那么线程B就会等待进入同步块,等待A 线程退出同步块,logObj 已经初始化了,B 线程进入同步块后发现logObj 不为null,退出同步块,不再初始化logObj 。 这样既实现了线程安全,又兼顾了效率,确实是很聪明的编码方式。但是问题来了,由于指令重排序的存在,会导致Log在完全初始化之前logObj就已经不为null。这样其他线程可能会得到未完全初始化的对象。

解决方法

  1. JDK1.5版本后扩展了volitile语义,可以保证上述代码的正确性,为此只要将logObj 声明为volitile即可(volitile之前只是保证内存的可见性而已)。

  2. 使用静态内部类。

加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。

并且jvm会保证类加载的线程安全问题,所以利用这个特性可以写出兼顾效率与保证线程安全的版本。

实现四(兼顾效率与线程安全的版本)

public class Singleton{
    static class LogHolder{
        static Log logObj = new Log();
    }

    public static Log getInstance(){
        return LogHolder.logObj;
    }
}

这样在Singleton类加载时,并不会加载LogHolder,也就不会初始化Log,如果有线程访问getInstance方法,那么jvm会首先加载LogHolder类,并保证初始化logObj,最后返回logObj

版权声明:本文为博主原创文章,未经博主允许不得转载。

设计模式-单例模式(饿汉式及懒汉式的Java实现)

标签:java   单例模式   设计模式   

原文地址:http://blog.csdn.net/zjx409/article/details/47264927

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!