设计模式之单件模式

一、含义

单件模式(又称单例模式)确保一个类只有一个实例,并提供一个全局访问点。

二、单件模式的经典实现

在Java中实现单件模式需要私有的构造器、一个静态方法和一个静态变量
使用Java实现单件模式如下:

package com.pattern.singleton;
 
public class Singleton
{
	private static Singleton uniqueInstance = null;
 
	//其他有用的实例变量
	
	//构造方法是私有的,所以在类外不能new出多个实例
	private Singleton()
	{
		//初始化其他变量
	}
 
	public static Singleton getInstance()
	{
		if (uniqueInstance == null)
		{
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}
}

只有在首次使用这个类的实例时才会产生这个“单例”,否则永远不会产生,这就是“延迟实例化(lazy instantiaze)”;

三、处理多线程

可以看出在多线程情况下,上述的getInstance方法可能会返回不同的实例(比如两个线程同时判断出uniqueInstance为null,接下来就会产生两个不同的实例),为了解决这种情况,可以使用以下方法:

1.使用synchronized关键字将getInstance()方法变成同步方法

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

使用synchronized可以迫使每个线程进入该方法之前,要要先等候其他线程离开该方法(synchronized锁定的是该类对应的Class对象),java提供的这种并发控制方法在此处可能会使性能严重降低:只有第一次执行getInstance()方法时才需要同步,之后每次调用这个方法时,同步都会成为累赘(并行执行改为串行执行),若getInstance()需要被频繁执行,则性能会大大降低。

2.使用“急切实例化”,而不用延迟实例化

public class Singleton
{
	//在JVM加载该类时已经创建此唯一的单件实例
	private static Singleton uniqueInstance = new Singleton();
	private Singleton()
	{
		
	}
         
	public static  Singleton getInstance()
	{
		return uniqueInstance;
	}
}

在静态初始化时创建单件,保证了线程安全。但是如果这个对象非常耗费资源,而在程序的执行过程中并没有使用到它,那就造成资源的浪费了。

3.用“双重检查加锁”,在getInstance()中减少使用同步

使用双重检查加锁(double-checked locking),首先检查实例是否已经创建了,如果尚未创建,才进行同步,这样一来既实现了延迟实例化又避免了多线程同步synchronized所产生的性能降低的问题,是一个不错的解决方法。

public class Singleton
{
	private volatile static  Singleton uniqueInstance = null;
	//其他
	
	private Singleton()
	{
		//其他
	}
 
	public static  Singleton getInstance()
	{
		if (uniqueInstance == null)
		{
			synchronized(Singleton.class)
			{
				if(uniqueInstance==null)
				{
					uniqueInstance = new Singleton();
				}
				
			}
		}
		return uniqueInstance;
	}
}

那么就具备了两层语义:
volatile关键词确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量。

  1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  2. 禁止进行指令重排序。

注意:双重检查加锁不适用于1.4及更早的版本,在1.4及更早版本中许多JVM对volatile关键字的实现会导致双重检查加锁的失效。

四、注意事项

  1. 如果不是采用第五版的Java 2,双重检查加锁实现可能会失效。
  2. 你可能使用多个类加载器,可能导致单件失效而产生多个实例。
    解决办法:自行指定类加载器,并指定同一个类加载器。
  3. 如果使用JVM 1.2或之前的版本,你必须建立单件注册表,以免垃圾回收器将单件回收。
  4. 确定性能和资源上的限制,然后小心的选择适当地方案来实现单件,以解决多线程的问题。
  5. 最好不要继承单件类。

全局变量与单例模式的区别

在java中,全局变量基本上就是对对象的静态(static)引用。在这样的情况下使用全局变量会有一些缺点。如果将对象赋值给一个全局变量,那么就必须在程序一开始时就创建好对象而不是延迟实例化,可能会造成资源的浪费,而单件模式可以实现延迟实例化;全局变量可以提供全局访问,但是不能确保只有一个实例,用许多全局变量指向许多小对象会造成命名空间(namespace)的污染,单件不鼓励这样的现象,但单件仍然可能被滥用。
参考:《Head First设计模式》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值