单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
在JAVA中实现单例,必须了解JAVA内存机制,JAVA中实例对象存在于堆内存中,若要实现单例,必须满足两个条件:
1.限制类实例化对象。即只能产生一个对象。
2.保证外部能够获取这个对象,否则对象创建将毫无意义
如要满足以上两个条件,可以将构造方法私有化,然后在类中提供一个静态方法获取类实例。代码如下
1 public class SingleTon {
2
3 private static final SingleTon single = new SingleTon();
4
5 private SingleTon() {
6
7 }
8
9 /**
10 * 获取一个单例对象。
11 * @return 返回SingleTon对象。
12 */
13 public static SingleTon getInstance() {
14 return single;
15 }
16
17 }
JVM加载SingleTon后,会对静态成员做默认初始化,此时new SingleTon()创建的对象会赋值给single,类只会被加载一次,即使多次调用getInstance方法,所返回的对象也不会改变。single字段在初始化的过程中,对象就创建了,所以以上案例的代码又称为饿汉式。从对象的生命周期来看,类一旦加载,对象会在堆中立即创建,会浪费内存空间,因此,又存在另外一种称为懒汉式的单例设计模式。代码如下:
1 public class SingleTonLazy {
2
3 private static SingleTonLazy single = null;
4
5 private SingleTonLazy() {
6
7 }
8
9 /**
10 * 获取一个单例对象。
11 * @return 返回SingleTon对象。
12 */
13 public static SingleTonLazy getInstance() {
14
15 if (single == null) {
16 single = new SingleTonLazy();
17 }
18
19 return single;
20
21 }
22
23 }
SingleTonLazy在被加载进方法区后,不会立即创建对象,而是直到getInstance方法被调用以后,对象才会被创建。这种方式可以节约内存空间,但是也存在着线程安全问题,当线程A执行到判断对象为null,此时线程B获得执行权,线程B判断对象为null,此时线程A重新获得执行权,创建对象,线程B恢复,继续创建对象。将代码修改如下,使用同步锁解决线程安全问题。
1 public class SingleTonLazy {
2
3 private static SingleTonLazy single = null;
4
5 private final static Lock lock = new ReentrantLock();
6
7 private SingleTonLazy() {
8
9 }
10
11 /**
12 * 获取一个单例对象。
13 * @return 返回SingleTon对象。
14 */
15 public static SingleTonLazy getInstance() {
16
17 if (single != null) {
18 return single;
19 }
20
21 lock.lock();
22 if (single == null) {
23 single = new SingleTonLazy();
24 }
25 lock.unlock();
26
27 return single;
28
29 }
30 }
总结:饿汉式与懒汉式各有优缺点,但是相对来说,占用内存空间比让cpu判断锁的开销要小,所以饿汉式更使用一些。