java 单例模式

2019-08-15  本文已影响0人  梦之马

写在前面:这篇文章主要不同之处在于提供了,
1、多线程测试,用于检查对象是否是单例
2、加入了我自己的理解

单例模式是创建型模式的一种,主要用户创建唯一的对象。
应用场景,比如数据库链接,window 任务控制器,日历对象等

懒加载模式

对象在使用的时候才进行加载,不使用不加载节省内存空间的作用

关键字 synchronized 的原子性使同一时刻仅有一个线程在执行,那么第一个获得锁的线程,进入 p1、p2 会进行实例化,第一个获得锁的线程释放锁之后,后面进入的线程 在 p1 处 false 则不会进行实例化。
从而保证了对象的单例性

public class Singleton {
    private static Singleton instance = null;

    //不允许实例化
    private Singleton() {
        try {
            //模拟耗时创建,比如链接建立等
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) { // p1
            instance = new Singleton();  // p2
        }
    }

从上面可以看出,在方法上加锁 synchronized ,会让每个次获取单例对象的时候,频繁地取锁和释放锁,会造成较大的开销(我也没试过,你们可以试试)

作为优化,我们需要缩小锁的范围,获取已创建的对象时,不用加锁,仅在创建对象的时候加锁。
所以双重检查的单例模式,第一重检查的是对象是否已创建,第二重检查多线程模式下是否已有线程创建对象

ps:加有我注释的代码可以删除

package com.github.songjiang951130.designpattern.singleton;

public class Singleton {
    private static Singleton instance = null;

    //不允许实例化
    private Singleton() {
        try {
            //模拟耗时创建,比如链接建立等
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static Singleton getInstance() {
        //第一重检查 检查对象是否创建成功,
        //1、没有则由线程去创建
        //2、有则不进入代码同步块中加快获取对象速度
        if (instance == null) {
            System.out.println(System.currentTimeMillis() + " " + Thread.currentThread().getId() + " " + Thread.currentThread().getName() + " synchronized status:" + (instance == null));
            /**
             * 当创建单例对象时间较长时(故意将构造函数sleep),会有多个线程在此处 等待获取锁 进入同步块(如下日志),同一时间仅有一个线程进入
             * id name
             * 26 Thread-14 synchronized status:true
             * 33 Thread-21 synchronized status:true
             * 35 Thread-23 synchronized status:true
             * 38 Thread-26 synchronized status:true
             * 39 Thread-27 synchronized status:true
             * 36 Thread-24 synchronized status:true
             * 31 Thread-19 synchronized status:false
             * 37 Thread-25 synchronized status:false
             * 32 Thread-20 synchronized status:false
             * 30 Thread-18 synchronized status:false
             */
            synchronized (Singleton.class) {
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread().getId() + " " + Thread.currentThread().getName() + " start status:" + (instance == null));
                //第二重检查,此处会有多个线程来创建,用于避免多个线程创建对象,破坏了单例模式
                if (instance == null) {
                    System.out.println(System.currentTimeMillis() + " " + Thread.currentThread().getId() + " " + Thread.currentThread().getName() + " create");
                    instance = new Singleton();
                    //此处对象创建完成后即可新进入第一重检查的获取
                }
                System.out.println(System.currentTimeMillis() + " " + Thread.currentThread().getId() + " " + Thread.currentThread().getName() + " end  status:" + (instance == null));
            }
        }
        return instance;
    }
}
    private static Singleton instance = null;
   // 此处有加volatile 使对象可见,测试了后没发现什么好处,暂时没加。主要是没做性能测试,懒得写了
    private static volatile Singleton instance = null;

测试用例:多线程测试

package com.github.songjiang951130.designpattern.singleton;

import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class SingletonTest {
    List<Singleton> list;

    @Test
    public void test() {
        int threadCount = 100;
        list = new ArrayList<Singleton>(threadCount);
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            new Thread(new work(countDownLatch)).start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //测试每个对象是否相等,第一次仅赋值
        Singleton singleton = null;
        for (Singleton element : list) {
            if (singleton == null) {
                singleton = element;
                continue;
            }
            Assert.assertEquals(singleton, element);
            singleton = element;
            System.out.println(element);
        }
        list = null;
    }

    public class work implements Runnable {
        CountDownLatch countDownLatch;

        public work(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            Singleton singleton = Singleton.getInstance();
            list.add(singleton);
            countDownLatch.countDown();
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读