单例模式有懒汉加载和饿汉加载两种方式。单例模式最关键是在于保证该类的对象只有一个,也就是只被new 了一次,所以私有化构造器是必须的。当然我们不考虑反射机制来创建对象,否则就不存在单例这么一说了。饿汉的优点在于不需要考虑线程安全问题,缺点在于无论程序是否使用到该对象,都已经创建了,增大了系统开销;懒汉则反之。
饿汉加载是直接声明并创建类的实例。
package singleton;
public class Singleton {
//饿汉模式,类加载即创建对象
public static final Singleton SINGLETON=new Singleton();
//私有化构造器
private Singleton() {
}
}
package singleton;
public class MainClass {
public static void main(String[] args) {
Singleton singleton1 = Singleton.SINGLETON;
Singleton singleton2 = Singleton.SINGLETON;
System.out.println(singleton1==singleton2);//true
}
}
结果singleton1和singleton2是同一引用,即单例。
懒汉模式,即对象第一次被调用时才去创建实例。
package singleton;
public class Singleton {
private static Singleton singleton;
//私有化构造器
private Singleton() {
}
//提供静态的共有方法对外提供对象
public static Singleton getInstance() {
if(singleton==null) {
singleton=new Singleton();
}
return singleton;
}
}
package singleton;
public class MainClass {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);//true
}
}
输出结果依旧是true.获取的对象是同一地址。但是不难看出上面的代码在多线程并发的情况下是不安全的。所以对它进行优化。
public static synchronized Singleton getInstance() {
if(singleton==null) {
singleton=new Singleton();
}
return singleton;
}
于是我们在方法上加synchronized锁,可以满足并发线程安全的要求,但及其影响效率,线程安全仅仅只需考虑第一次创建的时候。故再做优化。
public static Singleton getInstance() {
if(singleton==null) {
synchronized(Singleton.class) {
if(singleton==null) {
singleton=new Singleton();
}
}
}
return singleton;
}
这样优化只有在第一次对象为空的时候才会出现同步阻塞的现象。