1. 什么是单例模式?
确保一个类只有一个实例,并提供一个全局访问点
单例模式见名知意, 就是只有一个实例。在整个系统应用中不管获取多少次,拿到的都是同一个对象, 这就是单例模式。
2. 单例模式用来干什么,什么时候使用?
我们知道了单例模式的意思, 那么为什么要用这个单例模式呢?
答案很简单,在需要重复创建和销毁对象的时候使用单例模式,可以节省系统资源,比如工具类、DAO等这种在多个地方使用却只需要一个实例就可以完成操作的类,我们就可以把它设计为单例模式。
3. 怎么实现单例模式?
-
饿汉式
饿汉式,启动时就初始化完毕,不存在线程安全问题,实例不使用会消耗资源
package com.single.hungry;
public class Hungry {
private final static Hungry hunger = new Hungry();
private Hungry() {
}
public static Hungry getInstance() {
return hunger;
}
}
-
懒汉式
第一版:使用时初始化,但是无法保证线程安全,有可能两个线程同时进入到 if (null == lazyOne)判断里面,然后初始化两次。
package com.single.lazy;
public class LazyOne {
private static LazyOne lazyOne;
private LazyOne() {
System.out.println("init");
}
public static LazyOne getInstance() throws InterruptedException {
if (null == lazyOne) {
Thread.sleep(1000);
lazyOne = new LazyOne();
}
return lazyOne;
}
}
第二版:使用synchronized关键字,线程安全,每次访问都会加锁,效率不高
package com.single.lazy;
public class LazyTwo {
private static LazyTwo lazyTwo;
private LazyTwo() {
System.err.println("init");
}
public static synchronized LazyTwo getInstance() throws InterruptedException {
if (null == lazyTwo) {
lazyTwo = new LazyTwo();
}
return lazyTwo;
}
}
第三版:双重锁,线程安全
package com.single.lazy;
public class LazyFour {
private LazyFour(){
System.err.println("init");
}
//一个线程中对该变量的修改对其他线程可见
public volatile static LazyFour lazyFour = null;
public static LazyFour getInstance() {
if (lazyFour == null) {
synchronized (LazyFour.class) {
if (lazyFour == null) {
lazyFour = new LazyFour();
}
}
}
return lazyFour;
}
}
第四版:静态内部类实现,当调用LazyThree时,才会去初始化LazyHolder.LazyThree .lazy实例。
package com.single.lazy;
public class LazyThree {
private LazyThree() {
System.err.println("init");
}
public static final LazyThree getInstance() {
return LazyHolder.lazy;
}
public static class LazyHolder {
private static final LazyThree lazy = new LazyThree();
}
}
最后还有什么注册式单例(Spring中使用)、枚举单例就不贴代码了。