设计模式之单例模式

来源:互联网 发布:linux stage2存放在哪 编辑:程序博客网 时间:2024/05/16 17:54

1、定义:

一个类有且仅有一个实例,并且自行实例化向整个系统提供(参见百度百科解释)。是一种常用的设计模式。例如Windows中的任务管理器只能打开一个;一个系统可以有多个打印任务,但是只能有一个正在工作的任务;一个系统中只能有一个ID生成器或计时器……

2、特点:

(1)该类中只能有一个实例;

(2)必须自行创建这个实例;

(3)必须向系统提供一个可以访问该实例的方法;

3、实现角度:

(1)该类提供私有的构造函数;

(2)类的定义中含有该类的私有、静态的对象;

(3)该类中含有共有、静态的方法或函数来获取或创建该类本身私有、静态的对象;

4、形式:

懒汉式、饿汉式、登记式单例

(一)懒汉式单例模式(延迟加载)

public class SingletonDemo  {    // 该模式在第一次调用时即被实例化        private SingletonDemo () {}  // 私有的构造函数        private static SingletonDemo single=null;  // 该类的私有、静态的对象       public static SingletonDemo getInstance() {  // 共有、静态的方法以获取该类私有、静态的对象         if (single == null) {                 single = new SingletonDemo();           }            return single;      }  

单线程下访问该实例,此实现方式是没问题的,参见代码:

public class ThreadSingletonTest {public static void main(String[] args) {SingletonDemo single1=SingletonDemo.getInstance();SingletonDemo single2=SingletonDemo.getInstance();assert  single1==single2;}}
多线程下并发访问该单例,则存在线程安全问题,参见代码:
public class ThreadTest implements Runnable {    //存放单例对象,Set可避免存放重复元素      public Set<SingletonDemo> singles = new HashSet<SingletonDemo>();      @Override      public void run() {          //获取单例      SingletonDemo s = SingletonDemo.getInstance();          //添加单例          singles.add(s);      }  }

public class ThreadSingletonTest {public static void main(String[] args) {//SingletonDemo single1=SingletonDemo.getInstance();//SingletonDemo single2=SingletonDemo.getInstance();//assert  single1==single2;        ThreadTest t = new ThreadTest();          new Thread(t).start();          new Thread(t).start();          new Thread(t).start();          new Thread(t).start();          new Thread(t).start();           System.out.println(t.singles);  }}
输出结果为[com.fan.testDemo.SingletonDemo@66505655]或com.fan.testDemo.SingletonDemo@1e9f2094……。由此可见多线程下访问的不是单例,存在线程安全问题。

解决方式:

(一)在getInstance方法前加上线程同步

public class SingletonDemo {private SingletonDemo(){}; private static SingletonDemo single=null; public static synchronized SingletonDemo getInstance(){ if(single==null){single=new SingletonDemo();}return single;}}

此方式线程每次访问实例均需要判断锁,效率较低,故采用方式二

(二)双重锁结构

public class SingletonDemo {private SingletonDemo(){}; private static SingletonDemo single=null; public static SingletonDemo getInstance(){ // 如果前面的线程获取了单例对象,则后面的线程就不用访问锁了(不进同步代码块)synchronized (SingletonDemo.class) {  // 同步代码块使用的是该单例的的字节码文件对象if(single==null){single=new SingletonDemo();}return single;}}}

(三)静态内部类

public class SingletonDemo {        private static class InnerClass{           private static final SingletonDemo single= new SingletonDemo();        }        private SingletonDemo (){}        public static final SingletonDemo getInstance() {           return InnerClass.single;        }    }   
此方式较好。

(二)饿汉式单例模式

//饿汉式单例在类初始化时,单例对象已经自行实例化 public class SingletonTest {private SingletonTest(){};private static SingletonTest singletonTest=new SingletonTest();public static SingletonTest getInstance(){return singletonTest;}}

由于在 类加载时就已经完成了实例化的静态对象以供调用,所以毫无疑问是线程安全的。

(三)登记式单例模式

因为登记式单例模式在平时使用的极少,本人接触的较少,这里忽略。详情可以参考http://blog.csdn.net/lanzhizhuxia/article/details/7922977

(四)懒汉式和饿汉式异同









1 0
原创粉丝点击