`
DAOException
  • 浏览: 120971 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

单例模式初探

    博客分类:
  • java
阅读更多

当我们在系统中需要频繁使用一个公用的类的时候,我们更多的希望不用每次在调用的时候去实例化一个新的对象。在设计模式当中有这样一种设计模式——单例模式。

单例模式可以简单的分为饥汉模式和懒汉模式,我们来分别看一下两种模式的实现方式吧:

饥汉模式:

public class PrintMessage {

	private static PrintMessage pm = new PrintMessage();
	/**
	 * 私有化构造函数
	 */
	private PrintMessage(){}
	/**
	 * 获取全局唯一实例
	 * @return
	 */
	public synchronized static PrintMessage getInstance(){
		return pm;
	}
}

  懒汉模式:

 

public class PrintMessage {
	private static PrintMessage pm = null;
	/**
	 * 私有化构造函数
	 */
	private PrintMessage(){}
	/**
	 * 获取全局唯一实例
	 * @return
	 */
	public synchronized static PrintMessage getInstance(){
		if(pm == null){
			pm = new PrintMessage();
		}
		return pm;
	}
}

 

  两种模式可以同样实现在内存中生成唯一的对象,不过两者之间存在一定的区别,饥汉模式会在类装载的时候生成全局的唯一变量,而懒汉模式会在系统首次调用的时候生成全局唯一变量,一个是在系统初始化的时候耗费生成实例的时间,一个是在首次调用的时候耗费生成实例的时间。不过具体要看情况而选择不同的模式。

懒汉模式潜在中存在一定的危险性,当然这种危险性是相对的,就是假设在多个线程第一次同时调用该类,那么存在一种可能是生成多个对象,违反了单例的原则,不过这种可能性极低。我们有时候完全不必要考虑为了避免这一点安全性来给类加上更耗费资源的同步锁问题。

说到上面的同步,单例模式其实也存在一个多线程同步问题,因为单例的实现,目的就是在多线程调用情况下不过多的生成新的实力耗费资源。我们来看下面一段代码,代码用单例模式编写,顺序打印出一堆数据。

 

public class PrintMessage {
	private static PrintMessage pm = null;
	private int count = 0 ;
	/**
	 * 私有化构造函数
	 */
	private PrintMessage(){}
	/**
	 * 获取全局唯一实例
	 * @return
	 */
	public static PrintMessage getInstance(){
		if(pm == null){
			pm = new PrintMessage();
		}
		return pm;
	}
	/**
	 * 打印信息
	 * 
	 */
	public void print() {
		System.out.println(count);
		/*try {
			Thread.currentThread().sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		count++;
		
	}
}

 

  我们再模拟一个多线程去调用这个单例。看下输出结果:

 

public static void main(String[] args){
		for (int i = 0; i < 50; i++) {
			Thread t = new Thread(
			new Runnable(){
				public void run(){
					PrintMessage.getInstance().print();	
				}
			}		
			);
			t.start();
		}
	}

 

  我们可以很容易的看到打印出的结果有很多重复数据,我想大家应该可以看出来是典型的线程同步的问题。那么我们怎么避免这个问题呢?首先我想到再getInstance方法加上同步,同时只有一个对象获取该对象的实例。我们来看下下面的代码

 

public synchronized static PrintMessage getInstance(){
		if(pm == null){
			pm = new PrintMessage();
		}
		return pm;
	}

 

  继续执行多线程方法,我们发现结果依然不是我们希望的顺序递增。这是为什么呢?因为我们获取对象实例后,实际上已经获得了对象的一个映射,这是我们会释放该方法上的同步锁。所以依然没有对print方法起到同步的效果。我们来看下给print方法加上同步锁呢。

 

/**
	 * 打印信息
	 * 
	 */
	public synchronized void print() {
		System.out.println(count);
		/*try {
			Thread.currentThread().sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		count++;
		
	}

  继续执行先前的方法,怎么样,可以看到我们希望的顺序递增的效果了吧。当然不知道理解对不对,但是单例模式中的线程安全问题还是需要特别注意的。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics