设计模式(六)单例模式

单例模式在日常使用非常平凡

一般有4种写法

  • 懒汉式 也称为双重锁定
package com.zhy.pattern.singlton;
 
 
public class Singleton02
{
	private static Singleton02 instance;
 
	public static Singleton02 getInstance()
	{
		if (instance == null)
		{
			synchronized (Singleton02.class)
			{
				if (instance == null)
				{
					instance = new Singleton02();
				}
			}
		}
		return instance;
	}
}
  • 饿汉式 就是类加载就初始化了
package com.zhy.pattern.singlton;
 
public class Singleton
{
	private static Singleton instance = new Singleton();
	
	public static Singleton getInstance()
	{
		return instance ;
	}
}
  • 枚举
public enum Singleton03
{
    INSTANCE;
}
  • 使用一个持有类,主要是为了不在初始化的时候加载
package com.zhy.pattern.singlton;
 
public class Singleton04
{
	private static final class InstanceHolder
	{
		private static Singleton04 INSTANCE = new Singleton04();
	}
 
	public static Singleton04 getInstance()
	{
		return InstanceHolder.INSTANCE;
	}
}

这4种都是线程安全的

 

下面看一下单例模式的改进过程的例子

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8860649

写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上线后还可以帮助你分析数据。但是Java原生带有的System.out.println()方法却很少在真正的项目开发中使用,甚至像findbugs等代码检查工具还会认为使用System.out.println()是一个bug。

为什么作为Java新手神器的System.out.println(),到了真正项目开发当中会被唾弃呢?其实只要细细分析,你就会发现它的很多弊端。比如不可控制,所有的日志都会在项目上线后照常打印,从而降低运行效率;又或者不能将日志记录到本地文件,一旦打印被清除,日志将再也找不回来;再或者打印的内容没有Tag区分,你将很难辨别这一行日志是在哪个类里打印的。

你的leader也不是傻瓜,用System.out.println()的各项弊端他也清清楚楚,因此他今天给你的任务就是制作一个日志工具类,来提供更好的日志功能。不过你的leader人还不错,并没让你一开始就实现一个具备各项功能的牛逼日志工具类,只需要一个能够控制打印级别的日志工具就好。

第一个版本

public class LogUtil {
 
	public final int DEGUB = 0;
 
	public final int INFO = 1;
 
	public final int ERROR = 2;
 
	public final int NOTHING = 3;
 
	public int level = DEGUB;
 
	public void debug(String msg) {
		if (DEGUB >= level) {
			System.out.println(msg);
		}
	}
 
	public void info(String msg) {
		if (INFO >= level) {
			System.out.println(msg);
		}
	}
 
	public void error(String msg) {
		if (ERROR >= level) {
			System.out.println(msg);
		}
	}
 
}

使用时

new LogUtil().debug("Hello World");

可是打印这种事情是不区分对象的,这里每次需要打印日志的时候都需要new出一个新的LogUtil,太占用内存了,希望你可以将这个工具改成用单例模式实现

第二个版本

public class LogUtil {
 
	private static LogUtil sLogUtil;
 
	public final int DEGUB = 0;
 
	public final int INFO = 1;
 
	public final int ERROR = 2;
 
	public final int NOTHING = 3;
 
	public int level = DEGUB;
 
	private LogUtil() {
	}
 
	public static LogUtil getInstance() {
		if (sLogUtil == null) {
			sLogUtil = new LogUtil();
		}
		return sLogUtil;
	}
 
	public void debug(String msg) {
		if (DEGUB >= level) {
			System.out.println(msg);
		}
	}
 
	public void info(String msg) {
		if (INFO >= level) {
			System.out.println(msg);
		}
	}
 
	public void error(String msg) {
		if (ERROR >= level) {
			System.out.println(msg);
		}
	}
 
}

首先将LogUtil的构造函数私有化,这样就无法使用new关键字来创建LogUtil的实例了。然后使用一个sLogUtil私有静态变量来保存实例,并提供一个公有的getInstance方法用于获取LogUtil的实例,在这个方法里面判断如果sLogUtil为空,就new出一个新的LogUtil实例,否则就直接返回sLogUtil。这样就可以保证内存当中只会存在一个LogUtil的实例了。单例模式完工!这时打印日志的代码需要改成如下方式:

LogUtil.getInstance().debug("Hello World");

你将这个版本展示给你的leader瞧,他看后笑了笑,说:“虽然这看似是实现了单例模式,可是还存在着bug的哦。

 

你满腹狐疑,单例模式不都是这样实现的吗?还会有什么bug呢?

 

你的leader提示你,使用单例模式就是为了让这个类在内存中只能有一个实例的,可是你有考虑到在多线程中打印日志的情况吗?如下面代码所示:

	public static LogUtil getInstance() {
		if (sLogUtil == null) {
			sLogUtil = new LogUtil();
		}
		return sLogUtil;
	}

如果现在有两个线程同时在执行getInstance方法,第一个线程刚执行完第2行,还没执行第3行,这个时候第二个线程执行到了第2行,它会发现sLogUtil还是null,于是进入到了if判断里面。这样你的单例模式就失败了,因为创建了两个不同的实例。

第三个版本

为了保证线程安全 于是加了同步锁

	public synchronized static LogUtil getInstance() {
		if (sLogUtil == null) {
			sLogUtil = new LogUtil();
		}
		return sLogUtil;
	}

但是如果是在getInstance方法上加了一个synchronized,那么我每次去执行getInstace方法的时候都会受到同步锁的影响,这样运行的效率会降低,其实只需要在第一次创建LogUtil实例的时候加上同步锁就好了

第四个版本

首先将synchronized关键字从方法声明中去除,把它加入到方法体当中:

	public static LogUtil getInstance() {
		synchronized (LogUtil.class) {
			if (sLogUtil == null) {
				sLogUtil = new LogUtil();
			}
			return sLogUtil;
		}
	}
这样效果是和直接在方法上加synchronized完全一致的。然后在synchronized的外面再加一层判断,如下所示:
	public static LogUtil getInstance() {
		if (sLogUtil == null) {
			synchronized (LogUtil.class) {
				if (sLogUtil == null) {
					sLogUtil = new LogUtil();
				}
			}
		}
		return sLogUtil;
	}

代码改成这样之后,只有在sLogUtil还没被初始化的时候才会进入到第3行,然后加上同步锁。等sLogUtil一但初始化完成了,就再也走不到第3行了,这样执行getInstance方法也不会再受到同步锁的影响,效率上会有一定的提升。

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值