写在前面:这篇文章主要不同之处在于提供了,
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();
}
}
}
网友评论