第一种:
public class Singleton {
private Singleton() { }
private static Singleton singleton = null;
public static Singleton getInstance() {
if(singleton == null) {
return new Singleton();
}
return singleton;
}
}
这种方式在多线程环境下运行可能导致第一个线程在运行至函数体getInstance内部,还未返回时,第二个线程也运行至此,进而两个线程都进入到if判断中;
第二种:
public class Singleton2 {
private Singleton2() { }
private static Singleton2 singleton = null;
public static synchronized Singleton2 getInstance() {
if (singleton == null) {
return new Singleton2();
}
return singleton;
}
}
这种方式可以解决多线程问题,但是由于同步对象作用于方法上,导致多次访问时效率会比较低,因为单例模式只是在第一次加载时用到,后面便不需要;
第三种:
public class Singleton {
private Singleton() { }
private static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
这种方法在类装载时便被初始化,避免了线程同步问题,但是没有缺少lazy loading思想,即用到时才初始化,大型项目中如果类似的初始化较多时,初始加载速度比较缓慢,并且增加系统开销;
方法四:
public class Singleton {
private static Singleton singleton = null;
private Singleton() { }
static {
Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return singleton;
}
}
和方法3类似,没有lazy loading;
方法5:
public class Singleton5 {
private Singleton5() { }
private static Singleton5 singleton = null;
public static Singleton5 getInstance() {
if (singleton == null) {
synchronized(Singleton5.class){
if(singleton == null) {
singleton = new Singleton5();
}
}
}
return singleton;
}
}
双重校验,仅做一重校验仍然会出问题,因为如果线程1进入到if条件中new完对象还未返回时,如果此时线程2也进入到if中等待释放同步锁,则同样会导致new出两个对象,并且第一个对象将处于无引用状态。
方法6:
public class Singleton6 {
private Singleton6() { }
private static class Singleton {
private static Singleton6 singleton = new Singleton6();
}
public static Singleton6 getInstance() {
return Singleton.singleton;
}
}
使用内部类生成实例;
方法7:
public class Singleton7 {
private Singleton7() { }
private static Singleton7 singleton = null;
private static synchronized Singleton7 getSingleton() {
if(singleton == null)
singleton = new Singleton7();
return singleton;
}
public static Singleton7 getInstance() {
if(singleton == null) {
return getSingleton();
}
return singleton;
}
}
此种方式可以避免lazy loading,效率也可以保证,只有初次加载时才会调用函数getSingleton();此方法同方法5雷同。
测试方式:
public class TestSingleton implements Runnable {
public static void main(String args[]) {
Thread thread1 = new Thread(new TestSingleton());
Thread thread2 = new Thread(new TestSingleton());
Thread thread3 = new Thread(new TestSingleton());
thread1.start();
thread2.start();
thread3.start();
}
@Override
public void run() {
Singleton single = Singleton.getInstance();
System.out.println(single);
}
}
输出结果相同,则实例相同,否则,不同。