设计模式之禅(6)-单件模式

版权声明:需要引用、发表的朋友请与本人联系 https://blog.csdn.net/pbrlovejava/article/details/84619518


更多关于设计模式的文章请点击设计模式之禅(0)-目录页


单件模式是面向对象设计模式中常用的一种设计模式,它主要的作用是使得某个对象在全局程序中只有唯一的一个实例,并且在全局中只有一个创建实例的访问点。这种模式通常采用在线程池、连接池、单一记录器等的使用和实现中。

一、单件模式的实现

单件模式Singleton Pattern)在23种设计模式中属于比较简单的那一类,隶属于创建型模式。它的主要思想是在全局中只有一个获得实例的创建点,并且将构造函数私有,不能由外部程序随意地创建它的实例。
在这里插入图片描述

  • SingletonObject1
/**
 * @Description: 单件模式构造
 * @CreateDate: Created in 2018/11/29 10:54
 * @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
 */
public class SingletonObject1 {
    private static SingletonObject1 s;
    //构造函数私有,外部类无法直接创建该对象
    private SingletonObject1(){
    }

    //全局唯一的获得实例点
    public static SingletonObject1 getInstance(){
        //判断对象是否已经被创建,没有被创建则新建对象
        if( s == null){
            s = new SingletonObject1();
        }

        return  s;
    }


}

通过私有化构造器并且提供唯一的实例获得点,就可以实现SingletonObject在全局中只存在唯一的一个实例了。

     @Test
        public void fun1() {
            SingletonObject1 singletonObject1 = SingletonObject1.getInstance();
            SingletonObject1 singletonObject2 = SingletonObject1.getInstance();
            System.out.println(singletonObject1+"\n"+singletonObject2);
        }

在这里插入图片描述

二、在并发下单件模式的改进

2.1、使用闭锁测试单件模式的正确性

上面写的单件模式代码似乎已经可以在全局中只产生一个实例了,可是如果在多线程模式下,以上代码还能实现单件吗?我使用了闭锁来测试并发时是否仍然能获得唯一的单件类:【关于闭锁的使用可以阅读我的一篇文章:Java并发编程(9)-使用闭锁测试并发线程安全性

 @Test
    public void concurrentTest(){
        //开始闭锁
        CountDownLatch startLatch = new CountDownLatch(1);
        for (int i = 0; i < 5; i++) {
            new Thread() {
              public void run(){
                  try {
                      //线程运行至闭锁处等待
                      startLatch.await();
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  //5个线程并发执行任务
                 new MyTask().run();
                }

            }.start();
        }

        //线程全部集结在闭锁下,开锁
        startLatch.countDown();
    }

    //任务类
    public class MyTask implements Runnable{

        @Override
        public void run() {
            //获得单例类
            SingletonObject1 instance = SingletonObject1.getInstance();
            //打印地址
            System.out.println(instance);
        }
    }

在这个测试中,我只让5个线程去并发地获得单件类实例,可是却出现了问题,单件的实例并不唯一了:
在这里插入图片描述

2.2、使用同步锁改进单件模式

要使得单件模式在并发条件下仍然正确,最简单的方式就是使用同步锁来限制同时读取实例的线程数,这个做法很安全,但是会影响速度:

//全局唯一的获得实例点
   public static synchronized SingletonObject1 getInstance(){
       //判断对象是否已经被创建,没有被创建则新建对象
       if( s == null){
           s = new SingletonObject1();
       }

       return  s;
   }

2.3、使用二重检查加锁来改进单件模式

对于同步锁而言,还可以使用更加轻量的volatile关键字来实现线程之间对实例状态的检查加锁:

public class SingletonObject {
   private volatile static SingletonObject instance;

   //构造方法私有化,只能在本类中调用构造方法
   private SingletonObject(){

   }
   /**
    *@description 获得全局唯一的实例
    *@author arong
    *@date 2018/11/27
    *@param:
    *@return com.iteason.singletonPattern.SingletonObject
    */
   public static SingletonObject getInstance(){
   //双重检查加锁
       if(instance == null){
           synchronized (instance){
               instance = new SingletonObject();
           }
       }
           return instance;
   }
}

猜你喜欢

转载自blog.csdn.net/pbrlovejava/article/details/84619518