保证一个类中只有唯一的实例。
单例类图
饿汉式:浪费空间,线程安全
public class SingletonHungry {
//类中直接创建实例
private static SingletonHungry instance = new SingletonHungry();
//私有化构造
private SingletonHungry() {
}
//返回此类创建的实例
public static SingletonHungry getInstance() {
return instance;
}
}
//测试
public class SingletonTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(SingletonHungry.getInstance());
}
}).start();
System.out.println(SingletonHungry.getInstance());
}
}
//结果:
com.jun.Singleton.SingletonHungry@1d44bcfa
com.jun.Singleton.SingletonHungry@1d44bcfa
懒汉式:使用的时候创建。节省空间,存在多线程安全问题
public class SingletonLazy {
//维护一个指向本类的实例引用
private static SingletonLazy instance = null;
private SingletonLazy() {
}
public static SingletonLazy getInstance() {
if (instance == null) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new SingletonLazy();
}
return instance;
}
}
//测试
public class SingletonTest {
public static void main(String[] args) throws Exception{
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(SingletonLazy.getInstance());
}
}).start();
System.out.println(SingletonLazy.getInstance());
}
}
//结果:得到不同的实例,出现多线程安全问题
com.jun.Singleton.SingletonLazy@1d44bcfa
com.jun.Singleton.SingletonLazy@2a8dfc9
解决懒汉式安全问题:加同步,此方式会造成效率低下。
完美解决饿汉式空间浪费,懒汉式线程安全问题的终极方法:
public class SingletonHold {
//整一个内部类,在需要使用实例时,返回
private static class SingletonInnerHold {
private static SingletonHold instance = new SingletonHold();
}
private SingletonHold() {
System.out.println("创建hold");
}
public static SingletonHold getInstance() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return SingletonInnerHold.instance;
}
}
//测试
public static void main(String[] args) throws Exception{
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(SingletonHold.getInstance());
}
}).start();
System.out.println(SingletonHold.getInstance());
}
//结果
创建hold
com.jun.Singleton.SingletonHold@1d44bcfa
com.jun.Singleton.SingletonHold@1d44bcfa
内部类中创建实例,在使用的时候才会创建,解决了懒汉式的空间浪费。
也不会出现线程安全问题,完美。
单例模式静态内部类为何线程安全?
虚拟机会保证一个类的类构造器<clinit>()在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器<clinit>(),其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
特别需要注意的是,在这种情形下,其他线程虽然会被阻塞,但如果执行方法的那条线程退出后,其他线程在唤醒之后不会再次进入/执行方法,因为 在同一个类加载器下,一个类型只会被初始化一次。如果在一个类的方法中有耗时很长的操作,就可能造成多个线程阻塞,在实际应用中这种阻塞往往是隐藏的。