单例模式是最简单,很常见的设计模式。一般单例模式分为懒汉式和饿汉式。懒汉式涉及线程安全,而饿汉式在类加载时就已经生成实例,不涉及线程安全问题,但却不是懒加载。
除了懒汉式和饿汉式,还可以通过静态内部类和枚举创建单例模式。
单例模式用于只能有一个对象的场景。
/**
*懒汉式,线程不安全。当有多个线程同时执行getInstance时,
*很有可能出现多个实例。这种方式只适用于单线程的场景
*/
public class Singleton1 {
//构造方法必须私有,对外屏蔽使用new关键字来创建对象
private Singleton1(){}
private static Singleton1 singleton1;
public static Singleton1 getInstance() {
if(singleton1==null){
singleton1=new Singleton1();
}
return singleton1;
}
}
/**
*懒汉式,线程安全。和Singleton1相比,只在getInstance方法上使用synchronized关键字
*进行了修饰。保证了getInstance()方法是同步执行的。但这种方式效率很低,当有多个线程需要获取实例时,
*大多数线程只能等待操作getInstance()的线程释放锁。无论singleton2是否已经实例化,没有锁的线程
*获取实例时都得等待(比如singleton2已经实例化了,应该直接就能获取到实例,但其他线程获取实例时还得先判断锁,
*无锁才能获取,有锁还得等待。这样就造成了效率低下)
*/
class Singleton2{
private Singleton2(){}
private static Singleton2 singleton2;
public static synchronized Singleton2 getInstance() {
if(singleton2==null){
singleton2=new Singleton2();
}
return singleton2;
}
}
/**
*懒汉式,双重校验锁,在多线程的情况下可以保持高效。有效解决了Singleton2的低效率问题。
*当多个线程执行getInstance()获取实例时,先判断singleton3是否已经实例化,一旦实例化
*就直接获取实例,不用执行同步代码块来判断锁,也不会因为没有锁而进行等待,所以效率比Singleton2高。
*在getInstance()中有两次判断singleton3是否为空。第一次判断很简单,如果不为空则线程直接获取,
*为空则执行同步代码块创建实例。
*第二次判断是在同步代码块中。因为可能多个线程第一次判断singleton3为null,都在等待执行同步代码块来创建新
*实例。如果没有第二次判断,那么前面的线程执行完同步代码块,创建了新实例。后进入的线程再执行同步代码块,
*又一次创建实例,这样就不能实现单实例了。
*/
class Singleton3{
private Singleton3(){}
private volatile static Singleton3 singleton3;
public static Singleton3 getInstance(){
if(singleton3==null){
synchronized(Singleton3.class){
if(singleton3==null){
singleton3=new Singleton3();
}
}
}
return singleton3;
}
}
/**
*饿汉式,代码很简单,也最常用。这种方式在类加载时,直接就创建了对象。避免了多线程的同步问题,线程安全。
*但这种没有采用懒加载(lazy loading),容易产生垃圾对象。
*
*/
class Singleton4{
private Singleton4(){}
private static Singleton4 singleton4=new Singleton4();
public static Singleton4 getInstance() {
return singleton4;
}
}
/**
*采用静态内部类的方式实现单例模式。因为静态内部类只有在调用时才会被加载(不同于静态代码块在类加载时就执行),实现了懒加载,
*达到双检锁一样的目的。但是又避免了使用同步代码块的繁琐。
*这种方式,集成了饿汉式的简洁和双重检验懒汉式的线程安全。
*优点:懒加载,多线程安全。
*/
class Singleton5{
private Singleton5(){}
//静态内部类只有在被调用时才会加载,是实现懒加载的充分条件。
private static class SingletonHolder{
static final Singleton5 SINGLETON5=new Singleton5();
}
public static Singleton5 getInstance(){
return SingletonHolder.SINGLETON5;
}
}
/**
* 使用枚举类型创建单例模式,可以防止通过反射破坏单例(上面的几种方式,都可以通过反射机制破坏单例)
* 这是实现单例模式的最佳方式。
*/
class Singleton6{
private Singleton6(){
System.out.println("正在创建Singleton6对象");
}
public static Singleton6 getInstance(){
return MyEnum.INSTANCE.getInstance();
}
/**
*定义一个静态的内部枚举类型
*/
static enum MyEnum{
INSTANCE;
private Singleton6 singleton6;
//JVM会保证此方法绝对只调用一次,枚举的构造方法不能被反射
private MyEnum(){
singleton6=new Singleton6();
}
public Singleton6 getInstance(){
return singleton6;
}
}
}