单例模式(singleton)是常用的设计模式,它只允许一个类被实例化1次。实现机制,定义或声明一个本类类型的静态域用来保存本类的实例,将构造方法私有化,提供一个公有的静态方法用来返回本类的实例。
单例模式有多重实现方法。
例1,饿汉式
/**
* 单线程下饿汉单例模式
*/
package pattern.singleton.singleThread.hunger;
public class HungerSingleton {
private static HungerSingleton instance = new HungerSingleton();
private HungerSingleton() {
System.out.println("正在实例化HungerSingleton");
}
public static HungerSingleton getInstance() {
return instance;
}
public static void print() {
System.out.println("aaa");
}
}
此例中HungerSingleton保存了一个本类实例的引用。并提供一个静态方法返回此实例getInstance()。
主函数:
/**
* 单例模式实验
*/
package pattern.singleton;
import pattern.singleton.singleThread.hunger.HungerSingleton;
public class SingletonTest {
public static void main(String[] args) {
HungerSingleton singleton1;
System.out.println("声明了一个HungerSingleton");
HungerSingleton.print();
for (int i = 0; i < 10; i++) {
singleton1 = HungerSingleton.getInstance();
System.out.println("singleton1--" + i + " 是 " + singleton1);
}
}
}
运行结果:
------------------------------------Console--------------------------------------
声明了一个HungerSingleton
正在实例化HungerSingleton
aaa
singleton1--0 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--1 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--2 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--3 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--4 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--5 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--6 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--7 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--8 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
singleton1--9 是 pattern.singleton.singleThread.hunger.HungerSingleton@4f1d0d
--------------------------------------------------------------------------------------
从结果上可以看到HungerSingleton的构造方法只被调用了一次。for循环中得到的所有的HungerSingleton的实例的地址都是相同的,也就是HungerSingleton只有一个实例。
#################################################
例2:懒汉式
/**
* 单线程下懒汉单例模式
*/
package pattern.singleton.singleThread.lazy;
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
System.out.println("正在实例化LazySingleton");
}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
public static void print() {
System.out.println("aaa");
}
}
此例中静态域instance的初始值是null。在调用getInstance()中判断instance是否引用一个实例。否则构造一个实例。这样可以保证类在需要实例化时才构造这个实例。而不是在加载这个类时就实例化这个类。
主函数:
/**
* 单例模式实验
*/
package pattern.singleton;
import pattern.singleton.singleThread.lazy.LazySingleton;
public class SingletonTest {
public static void main(String[] args) {
LazySingleton singleton1;
System.out.println("声明了一个LazySingleton");
LazySingleton.print();
for (int i = 0; i < 10; i++) {
singleton1 = LazySingleton.getInstance();
System.out.println("singleton1--" + i + " 是 " + singleton1);
}
}
}
运行结果:
------------------------------------Console--------------------------------------
声明了一个LazySingleton
aaa
正在实例化LazySingleton
singleton1--0 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--1 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--2 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--3 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--4 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--5 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--6 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--7 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--8 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
singleton1--9 是 pattern.singleton.singleThread.lazy.LazySingleton@4f1d0d
--------------------------------------------------------------------------------------
跟例1比较,可以看出在调用静态方法print()时。LazySingleton类并没有构造实例,而是在for循环中第一次构造。并且仍能保证单实例。
#################################################
多线程下的单例问题
改造一下例1:
/**
* 多线程下饿汉单例
*/
package pattern.singleton.multiThread.hunger;
public class HungerSingletonT {
private static HungerSingletonT instance = new HungerSingletonT();
private HungerSingletonT() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在实例化HungerSingletonT");
}
public static HungerSingletonT getInstance() {
return instance;
}
public static void print(String threadName) {
System.out.println(threadName + " aaa");
}
}
在构造方法中增加一个睡眠。
测试类:
package pattern.singleton.multiThread;
import pattern.singleton.multiThread.hunger.HungerSingletonT;
public class MultiThreadTest {
public static void main(String[] args) {
SingletonUser s1 = new SingletonUser("s1");
SingletonUser s2 = new SingletonUser("s2");
}
}
class SingletonUser implements Runnable {
private String threadName;
public SingletonUser(String threadName) {
this.threadName = threadName;
Thread t = new Thread(this, threadName);
t.start();
}
@Override
public void run() {
HungerSingletonT singleton;
System.out.println("线程 " + threadName + " 声明了一个singleton");
HungerSingletonT.print(threadName);
for (int i = 0; i < 10; i++) {
singleton = HungerSingletonT.getInstance();
System.out.println(threadName + " : singleton--" + i + " 是 " + singleton);
}
}
}
运行结果:
------------------------------------Console--------------------------------------
线程 s2 声明了一个singleton
线程 s1 声明了一个singleton
正在实例化HungerSingletonT
s2 aaa
s2 : singleton--0 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--1 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--2 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--3 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--4 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--5 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--6 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--7 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 aaa
s1 : singleton--0 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--8 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s2 : singleton--9 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--1 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--2 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--3 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--4 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--5 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--6 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--7 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--8 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
s1 : singleton--9 是 pattern.singleton.multiThread.hunger.HungerSingletonT@10b30a7
--------------------------------------------------------------------------------------
JVM的运行机制可以保证一个类在被加载的过程中是线程互斥的。饿汉的先行加载使得HungerSingletonT在第一次加载时实例化已完成。所以饿汉式单例是线程安全的。
#################################################
多线程下懒汉式单例问题。
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
设有线程t1和t2,t1在判断完instance==null之后进入到实例化LazySingleton,此时t2也执行到这里,判断instance==null。由于t1的实例化尚未完成,此时instance==null仍为true。所以t2顺利进行new LazySingleton()。于是LazySingleton被实例化了两次。所以懒汉式单例在多线程下是不安全的。
如下例所示:
/**
* 多线程下的懒汉式单例
*/
package pattern.singleton.multiThread.lazy;
public class LazySingletonT {
private static LazySingletonT instance = null;
private LazySingletonT(String str) {
System.out.println(str + " 正在实例化LazySingletonT");
}
public static LazySingletonT getInstance(String str) {
if (instance == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazySingletonT(str);
}
return instance;
}
public static void print(String str) {
System.out.println(str + " aaa");
}
}
测试类:
package pattern.singleton.multiThread;
import pattern.singleton.multiThread.lazy.LazySingletonT;
public class MultiThreadTest {
public static void main(String[] args) {
SingletonUser s1 = new SingletonUser("s1");
SingletonUser s2 = new SingletonUser("s2");
}
}
class SingletonUser implements Runnable {
private String threadName;
public SingletonUser(String threadName) {
this.threadName = threadName;
Thread t = new Thread(this, threadName);
t.start();
}
@Override
public void run() {
LazySingletonT singleton;
System.out.println("线程 " + threadName + " 声明了一个singleton");
LazySingletonT.print(threadName);
for (int i = 0; i < 10; i++) {
singleton = LazySingletonT.getInstance(threadName);
System.out.println(threadName + " : singleton--" + i + " 是 " + singleton);
}
}
}
运行结果:
------------------------------------Console--------------------------------------
线程 s2 声明了一个singleton
线程 s1 声明了一个singleton
s2 aaa
s1 aaa
s1 正在实例化LazySingletonT
s1 : singleton--0 是 pattern.singleton.multiThread.lazy.LazySingletonT@10b30a7
s1 : singleton--1 是 pattern.singleton.multiThread.lazy.LazySingletonT@10b30a7
s2 正在实例化LazySingletonT
s2 : singleton--0 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--2 是 pattern.singleton.multiThread.lazy.LazySingletonT@10b30a7
s1 : singleton--3 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--1 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--2 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--3 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--4 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--5 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--6 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--7 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--8 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--9 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--4 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--5 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--6 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--7 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--8 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--9 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
--------------------------------------------------------------------------------------
不难看出s1和s2分别实例化了一次LazySingletonT。
多线程下如何实现单例模式的延迟加载。我们先来改进一下懒汉式单例,给他增加一个锁synchronized。
例3:
public static synchronized LazySingletonT getInstance(String str) {
if (instance == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazySingletonT(str);
}
return instance;
}
运行结果:
------------------------------------Console--------------------------------------
线程 s2 声明了一个singleton
线程 s1 声明了一个singleton
s2 aaa
s1 aaa
s2 正在实例化LazySingletonT
s2 : singleton--0 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--1 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--2 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--3 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--4 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--5 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--6 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--7 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--8 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s2 : singleton--9 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--0 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--1 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--2 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--3 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--4 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--5 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--6 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--7 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--8 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
s1 : singleton--9 是 pattern.singleton.multiThread.lazy.LazySingletonT@1a758cb
--------------------------------------------------------------------------------------
看样子问题得到解决了。但新的问题是每一次调用getInstance都必须得到LazySingletonT的锁才行。而事实上只有第一次实例化时才需要锁住LazySingletonT,其他的时候getInstance只是返回已存在的LazySingletonT实例的引用,没必要获得锁。此例虽然解决了单例的互斥问题,但却影响了性能。
#################################################
整个方法需要获得对象锁会影响性能,那么只把实例化的部分放到同步块中会如何呢。
例4:双检锁
/**
* 双检锁单例模式
*/
package pattern.singleton.multiThread.lazy;
public class DCLSingleton {
private static DCLSingleton instance = null;
private DCLSingleton(String str) {
System.out.println("线程 " + str + " 正在实例化DCLSingleton");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void print(String str) {
System.out.println(str + " aaa");
}
public static DCLSingleton getInstance(String str) {
if (instance == null) {
synchronized (DCLSingleton.class) {
if (instance == null) {
instance = new DCLSingleton(str);
}
}
}
return instance;
}
}
这里很多帖子这样写synchronized(instance),这会抛出空指针异常(NullPointerException)。因为此时instance是空。线程无法获得对象锁。
测试类:
package pattern.singleton.multiThread;
import pattern.singleton.multiThread.lazy.DCLSingleton;
public class MultiThreadTest {
public static void main(String[] args) {
SingletonUser s1 = new SingletonUser("s1");
SingletonUser s2 = new SingletonUser("s2");
}
}
class SingletonUser implements Runnable {
private String threadName;
public SingletonUser(String threadName) {
this.threadName = threadName;
Thread t = new Thread(this, threadName);
t.start();
}
@Override
public void run() {
DCLSingleton singleton;
System.out.println("线程 " + threadName + " 声明了一个singleton");
DCLSingleton.print(threadName);
for (int i = 0; i < 10; i++) {
singleton = DCLSingleton.getInstance(threadName);
System.out.println(threadName + " : singleton--" + i + " 是 " + singleton);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
------------------------------------Console--------------------------------------
线程 s1 声明了一个singleton
线程 s2 声明了一个singleton
s1 aaa
线程 s1 正在实例化DCLSingleton
s2 aaa
s2 : singleton--0 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--0 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--1 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--1 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--2 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--2 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--3 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--3 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--4 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--4 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--5 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--5 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--6 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--6 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--7 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--7 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--8 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--8 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s2 : singleton--9 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
s1 : singleton--9 是 pattern.singleton.multiThread.lazy.DCLSingleton@10b30a7
--------------------------------------------------------------------------------------
s1,s2两个线程。s1第一次判断instance==null为true,获得对象锁。再次判断instance==null仍为true,开始实例化DCLSingleton。此时s2第一次判断instance==null,由于instance尚未实例化完成,结果仍是true,s2等待对象锁。直到线程s1执行实例化完成释放锁,此时instance不再是空。JVM不保证new语句和赋值语句的执行顺序,但都在同步块中,JVM可以保证这些语句都执行完毕才释放锁。所以,当s2获得对象锁时进行第二次判断instance==null,此时的结果为false。s2不再实例化DCLSingleton。双检锁机制可以实现单例模式。
#################################################
双检锁经过了多次判断,而且仍然有等待的时间。事实上饿汉式已经提供了一个互斥问题的解决方法。就是利用JVM本身的对于类加载过程是互斥的机制。将问题交给虚拟机来优化,提高性能。所要解决的就是延迟加载的问题。就是instance=new Singleton()的new操作只有在需要时才执行。可以把它放在一个类里,这个类在第一次加载时才会执行new操作。内部类可以胜任这个工作。
例5:
package pattern.singleton.multiThread.lazy;
public class ICSingleton {
private ICSingleton() {
System.out.println("正在实例化ICSingleton");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class InnerHandle {
private static ICSingleton instance = new ICSingleton();
}
public static ICSingleton getInstance() {
return InnerHandle.instance;
}
public static void print(String str) {
System.out.println(str + " aaa");
}
}
测试类:
package pattern.singleton.multiThread;
import pattern.singleton.multiThread.lazy.ICSingleton;
public class MultiThreadTest {
public static void main(String[] args) {
SingletonUser s1 = new SingletonUser("s1");
SingletonUser s2 = new SingletonUser("s2");
}
}
class SingletonUser implements Runnable {
private String threadName;
public SingletonUser(String threadName) {
this.threadName = threadName;
Thread t = new Thread(this, threadName);
t.start();
}
@Override
public void run() {
ICSingleton singleton;
System.out.println("线程 " + threadName + " 声明了一个singleton");
ICSingleton.print(threadName);
for (int i = 0; i < 10; i++) {
singleton = ICSingleton.getInstance();
System.out.println(threadName + " : singleton--" + i + " 是 " + singleton);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
------------------------------------Console--------------------------------------
线程 s1 声明了一个singleton
线程 s2 声明了一个singleton
s1 aaa
正在实例化ICSingleton
s2 aaa
s1 : singleton--0 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--0 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--1 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--1 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--2 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--2 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--3 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--3 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--4 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--4 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--5 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--5 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--6 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--6 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--7 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--7 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--8 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--8 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s2 : singleton--9 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
s1 : singleton--9 是 pattern.singleton.multiThread.lazy.ICSingleton@10b30a7
--------------------------------------------------------------------------------------
把instance放到一个内部类中。instance不会在ICSingleton加载时初始化。直到调用getInstance方法时,加载内部类InnerHandle才会初始化instance。类在加载时是线程互斥的。这样既保证了ICSingleton的单实例也可以实现延迟加载。