什么是单例设计模式? 顾名思义,只有一个实例。 单例模式它主要是确保一个类只有一个实例,并且可以提供一个全局的访问点。 废话少说,直接上干货了~
单例模式之饿汉式 所谓饿汉式,顾名思义,“ 它很饿 ”。所以说,它一旦被加载进来,就会直接实例化一个对象。 例如:
class Singleton {
private static final Singleton SINGLE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return SINGLE;
}
public void print() {
System.out.println("hello,我是饿汉~ 哇咔咔~");
}
}
接下来编写一个测试类
@Test
public void TestSingleton() {
Singleton s = Singleton.getInstance();
s.print();
}
运行效果:
结果异常顺利,就这样结束了吗?肯定不会就这样简单啦!我们再来看一下它在多线程的情况下运行效果如何?
class Singleton {
private static final Singleton SINGLE = new Singleton();
private Singleton() {
System.out.println("我出来啦~");
}
public static Singleton getInstance() {
return SINGLE;
}
public void print() {
System.out.println("hello,我是饿汉~ 哇咔咔~");
}
}
测试类:
@Test
public void TestSingleton() {
for (int i = 0; i < 3; i++) {
new Thread(()->{
Singleton s = Singleton.getInstance();
s.print();
}).start();
}
}
结果: 可以看到,测试结果还是异常丝滑。
那让我们再来看看它兄弟 --> 单例模式之懒汉式 一听他名字,就知道它比较懒,肯定一上来就不会进行实例化,而是先判断,先问一下你有对象了吗?如果没有的话,再给你分配。 所以说,它的写法应该是:
class Singletonss{
private static Singletonss SING = null;
private Singletonss() {
System.out.println("我是懒汉,我出来啦~");
}
public static Singletonss getInstance() {
if(SING == null) {
SING = new Singletonss();
}
return SING;
}
public void print() {
System.out.println("hello,我是懒汉~ 哇咔咔~");
}
}
下面是一个测试类:
@Test
public void TestSingleton() {
Singletonss s = Singletonss.getInstance();
s.print();
}
看一下运行结果: 貌似是一切正常,风平浪静,没什么太大问题~ 然后,让我们再来偷偷瞅一下它在多线程的情况下是什么妖怪吧~ 还是用上面的懒汉式代码,然后编写一个测试
@Test
public void TestSingleton() {
for(int i=0;i<3;i++) {
new Thread(()->{
Singletonss s = Singletonss.getInstance();
s.print();
}).start();
}
}
是不是被突然跳出来的饿汉吓到了,我才开启了三个线程,他竟然出来了三次~ 让我们分析一下是什么原因吧!可能细心的小伙伴们已经想到,多线程情况下,每个线程都会竞相抢夺资源,所以说第一个线程它在执行if判断但还未进行new的时候,别的线程也可能进入了if判断,所以说就会出现饿汉单例被实例化多次的情况,如何解决勒,有的人可能回想直接给那个方法加synchronized关键字,每次只允许一个线程进入。没错,这样是可以解决掉这问题,但是,如果给方法加了同步,也就意味着,线程资源来到这之后,只能先进入一个,别的需要排队等待这,如果只有一两个线程还好,如果说有几千个线程在这,无疑这样会大大浪费了资源,拖慢了程序的进度,所以这种方法不是最优的。那如果是给if判断加同步嘞?
public static Singletonss getInstance() {
synchronized (Singletonss.class) {
if(SING == null) {
SING = new Singletonss();
}
}
return SING;
}
感觉上还是蛮好的,但是与上个方法没太大区别。那,这,,,咋整哇~ 其实我们可以让每个先到的线程先进入if判断,然后开启同步,然后再进行一次判断,让先来的去实例化,后来的一看已经被实例化好了,就可以直接返回了。这样可以很大程度上减少线程等待时间。具体实现代码如下:
class Singletons {
private static volatile Singletons single = null;
private Singletons() {
System.out.println("我是懒汉,我出来啦~");
}
public static Singletons getInstance() {
if (single == null) {
synchronized (Singleton.class) {
if (single == null) {
single = new Singletons();
}
}
}
return single;
}
public void print() {
System.out.println("hello,我是懒汉~ 哇咔咔~");
}
}
测试类:
@Test
public void TestSingleton() {
for(int i=0;i<3;i++) {
new Thread(()->{
Singletons s = Singletons.getInstance();
s.print();
}).start();
}
}
再来看一下与运行结果: 一切正常