0%

单例模式

[toc]

单例模式,顾名思义, 就是一个系统内只提供一个单例,只有一个静态getInstance()方法。

  • 单例模式的类,其构造函数是private的,禁止外部去new

deb5ab479e94e371f07f770c11c80a0b202a07c4


Q: 讲一下单例模式的优点?#

A:

  • 在内存中只有一个对象,节省内存空间;

  • 避免频繁的创建销毁对象,可以提高性能;

  • 避免对共享资源的多重占用,简化访问;

  • 为整个系统提供一个全局访问点。


Q: 讲一下单例模式的缺点?#

A:

  • 不适用于变化频繁的对象;
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;

单例的几种java实现#

Q: 下面的饿汉单例实现有什么问题?#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 饿汉式单例
public class Singleton1 {

// 指向自己实例的私有静态引用,主动创建
private static Singleton1 singleton1 = new Singleton1();

// 私有的构造方法
private Singleton1(){}

// 以自己实例为返回值的静态的公有方法,静态工厂方法
public static Singleton1 getSingleton1(){
return singleton1;
}
}

A:
会造成启动速度变慢。 如果这个单例对象比较大或者多的话。 例如springbean的单例,如果都设置成饿汉式,则启动会很慢。


Q: 下面的懒汉式单例虽然能避免启动速度过慢,但这个代码有什么问题?#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 懒汉式单例
public class Singleton2 {

// 指向自己实例的私有静态引用
private static Singleton2 singleton2;

// 私有的构造方法
private Singleton2(){}

// 以自己实例为返回值的静态的公有方法,静态工厂方法
public static Singleton2 getSingleton2(){
// 被动创建,在真正需要使用时才去创建
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
}

A:
一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式


Q: 讲一下懒汉加载如何避免上述的问题?#

A:
使用双重加锁, 先判空, 判空后再加锁判断, 拿到锁之后再进行new
0b16110f2c2e97166c7998baf7ebdb228d00c4cb


Q: 可以不用锁实现java的单例延迟加载吗?#

A:
可以, 使用内部类的静态初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
private Singleton() {
//实例的初始化
}

static class SingletonHolder {
static Singleton instance = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.instance;
}
}

原因是因为内部类的加载和初始化本身是有用到类锁的,而内部类只会用到的时候加载, 不会提前被扫描进来。因此不会出现重复new两次, 但能够保证用到的时候才生成。