`
孙海友
  • 浏览: 24081 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

JDK设计模式应用之——单例模式(Singleton)

阅读更多

JDK设计模式应用——单例模式(Singleton)

    《JDK源码分析》的分支,讲解设计模式在jdk中使用。

    我们从三个方面讲述,一是:jdk源码中的设计模式;二是:讲解设计模式(UML图);三是:实现我们自己的设计模式代码。今天带来最简单的设计模式——单例模式(Singleton)

 

一、jdk源码中的设计模式

 

   我们先看java.lang包下的class Runtime

public class Runtime {

    private Runtime() {}//私有的构造方法

    //Runtime类的私有静态实例
    private static Runtime currentRuntime = new Runtime();

    //获得静态实例的唯一方法
    public static Runtime getRuntime() {
        return currentRuntime;
    }

}

    解释:

   (1)存在私有的构造函数,不能通过new来得到类的实例。

   (2)私有的静态实例,一个class只能对应唯一的对象。

   (3)只能通过:类.get方法获得这个唯一的对象实例。

 

     我们可以通过如下代码验证

Runtime r1 = Runtime.getRuntime();
Runtime r2 = Runtime.getRuntime();
//“==”为地址判断,为true表示:r1与r2指向同一对象。
System.out.println(r1 == r2);

   结果显然为:true

 

 

二、讲解设计模式(UML图)

 

                     

   上面是Singleton的UML图。

   单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。(来源——百度百科)

   单例模式的思想使用在很多方面,比如:一台计算机可以有若干打印机,但是一个时间只能有一个Printer Spooler,避免两个文件同时输入到打印机中。

   单例模式满足三个条件:

   (1)某个类只能有一个实例。

   (2)类必须自行创建这个类的实例(创建静态成员变量或者在类的静态方法中生成实例)。

   (3)类必须自行向整个系统提供这个实例。

 

 

 三、实现我们自己的设计模式

 

    我们实现单例模式分为两种:1、静态成员变量  2、静态方法(有人称为饿汉式和懒汉式

   1、静态成员变量

public class SingletonTest
{
        //在类里面实例化静态成员变量
	private static SingletonTest singletonTest = new SingletonTest();

	private SingletonTest()
	{
	}

	public static SingletonTest getInstance()
	{
		return singletonTest;
	}
}

 

 2、静态方法(最常用的方法)

public class SingletonTest
{
	private static SingletonTest singletonTest = null;

	private SingletonTest()
	{
	}

	public static SingletonTest getInstance()
	{
		//在类的静态方法中实例化单例
		if (null == singletonTest)
		{
			singletonTest = new SingletonTest();
		}

		return singletonTest;
	}
}

 

3、第二种方法构建单例模式是最常用的,但是在并发程序中会导致单例模式失效。

   如下面的代码所示:

public class SingletonTest
{
	private static SingletonTest singletonTest = null;

	private SingletonTest()
	{
	}

	public static SingletonTest getInstance()
	{
		if (null == singletonTest)
		{
			try
			{
				Thread.sleep((long) (Math.random() * 4000));
			} catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			singletonTest = new SingletonTest();
		}

		return singletonTest;
	}

	public static void main(String[] args)
	{
		// 打印出两个对象
		new ThreadTest().start();
		new ThreadTest().start();
	}
}

class ThreadTest extends Thread
{
	@Override
	public void run()
	{
		System.out.println(SingletonTest.getInstance());
	}
}

   输出的结果是:不是同一个对象,单例模式失效了。

com.shy.test.SingletonTest@55fe910c
com.shy.test.SingletonTest@3be4d6ef

   对于单例模式(Singleton)来说,如果在 getInstance()方法中生成 Singleton 实例则可能会产生同步问题,即可能会生成两个不同的对象。

 

  解释:

   当两个线程同时执行到getInstance()方法时,此时singletonTest为null,两个线程都会分别实例化出一个对象,因此出现了两个对象。

  解决方法:(使用synchronized解决同步问题)

 

public class SingletonTest
{
	private static SingletonTest singletonTest = null;

	private SingletonTest()
	{
	}

	public static SingletonTest getInstance()
	{
		synchronized (SingletonTest.class)
		{
			if (null == singletonTest)
			{

				singletonTest = new SingletonTest();

			}
		}

		return singletonTest;
	}

}

 

 

 

总结:Java中单例模式(Singleton Pattern)定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”

  • 大小: 8 KB
1
3
分享到:
评论
5 楼 孙海友 2014-03-26  
fisher123 写道
你这同步的解决办法也有问题的。比如2个线程同时进入到if(null==instance),此时都会去创建对象实例。虽然有同步关键字,但是在IF的时候,并没有进行排队。


其实最好的写法应该是这样的。
public class IndexNotify {
private IndexNotify() {}

private static class IndexNotifyHolder {
static IndexNotify instance = new IndexNotify();
}

public static IndexNotify getInstance() {
return IndexNotifyHolder.instance;
}
        }

有道理,我更新一篇博客,改进一下!
4 楼 孙海友 2014-03-26  
fisher123 写道
fisher123 写道
你这同步的解决办法也有问题的。比如2个线程同时进入到if(null==instance),此时都会去创建对象实例。虽然有同步关键字,但是在IF的时候,并没有进行排队。


其实最好的写法应该是这样的。
public class IndexNotify {
private IndexNotify() {}

private static class IndexNotifyHolder {
static IndexNotify instance = new IndexNotify();
}

public static IndexNotify getInstance() {
return IndexNotifyHolder.instance;
}
        }

因为静态内部类只有在使用的时候才会被Classloader 加载。而JVM 类加载的时候又是线程安全的。

IF判断要放到同步块中
3 楼 fisher123 2014-03-25  
fisher123 写道
你这同步的解决办法也有问题的。比如2个线程同时进入到if(null==instance),此时都会去创建对象实例。虽然有同步关键字,但是在IF的时候,并没有进行排队。


其实最好的写法应该是这样的。
public class IndexNotify {
private IndexNotify() {}

private static class IndexNotifyHolder {
static IndexNotify instance = new IndexNotify();
}

public static IndexNotify getInstance() {
return IndexNotifyHolder.instance;
}
        }

因为静态内部类只有在使用的时候才会被Classloader 加载。而JVM 类加载的时候又是线程安全的。
2 楼 fisher123 2014-03-25  
你这同步的解决办法也有问题的。比如2个线程同时进入到if(null==instance),此时都会去创建对象实例。虽然有同步关键字,但是在IF的时候,并没有进行排队。


其实最好的写法应该是这样的。
public class IndexNotify {
private IndexNotify() {}

private static class IndexNotifyHolder {
static IndexNotify instance = new IndexNotify();
}

public static IndexNotify getInstance() {
return IndexNotifyHolder.instance;
}
        }
1 楼 tomakemyself 2014-03-25  
                   

相关推荐

Global site tag (gtag.js) - Google Analytics