单例singleton:保证一个类只有一个实例。
实现单例模式的基本思想是:1将构造器私有化(private);2、在类的内部创建对象;3、向外暴露一个静态的公共方法。除了使用enum,其余的方法均是以上的三个环节。
饿汉式
public class Singleton {
// 私有化构造器
private Singleton() {
}
// 在类中声明并且实例化对象。
private static final Singleton SINGLETON_INSTANCE= new Singleton();
// 方法需要时静态的,因为该类不可被别人实例化,
// 想要获取该类的对象时,需要Singleton.getSingleton()来获取。
public static Singleton getSingleton() {
return SINGLETON_INSTANCE;
}
}
可以看到这种方式,保证了单例,并且在类加载初始化的时候就完成了实例化,避免了线程同步安全问题。但是存在着内存浪费的问题。
懒汉式
可以发现,该方法实现了让真正需要该对象时才实例化(起到了lazy loading 的效果)。但是可能存在多个线程同时调用getSington()方法,并且同时执行singleton = new Singleton()语句,从而破坏单例原则。所以该方法只适合单线程。
可以对上述代码改进,将方法设置成synchronized
public class Singleton {
// 私有化构造器
private Singleton() {
}
// 在类中声明并且实例化对象。
private static Singleton singleton;
// 方法需要时静态的,因为该类不可被别人实例化,
// 想要获取该类的对象时,需要Singleton.getSingleton()来获取。
//用synchronized来保证方法的同步安全,即同时只能一个线程里的方法能执行该方法。
public static synchronized Singleton getSingleton() {
// 当发现singleton还没有实例化的时候,进行实例化。
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
由于同时只能有一个线程执行该方法,会导致程序的执行效率变低。
双重检查 Double—Check
public class Singleton {
// 私有化构造器
private Singleton() {
}
// 在类中声明并且实例化对象。并设置成volatile;
private static volatile Singleton singleton;
// 方法需要时静态的,因为该类不可被别人实例化,
// 想要获取该类的对象时,需要Singleton.getSingleton()来获取。
public static Singleton getSingleton() {
// 当发现singleton还没有实例化的时候,进行实例化。
// 第一次判断;
if(singleton == null) {
synchronized (Singleton.class) {
if(singleton ==null) { // 第二次判断,保证只有一个对象会被创建。
singleton = new Singleton();
}
}
}
return singleton;
}
}
双重检查可以看做是对懒汉式的一种改进,将同步内容从方法层面改到方法里面的执行语句,从而提高了程序的执行效率。
将对象放入静态内部类
public class Singleton {
// 私有化构造器
private Singleton() {
}
// 将对象放入到静态内部类中,
private static class SingletonInstance{
private static final Singleton SINGLETON = new Singleton();
}
// 当第一次使用getSingleton方法时才会去实例化静态内部类,从而实例化对象。
public static Singleton getSingleton() {
return SingletonInstance.SINGLETON;
}
}
该方法利用了java的类加载机制,不仅保证了单例原则,还实现了延迟加载。
枚举类型
enum Singleton{
INSTANCE; //属性
void method() {
System.out.println("枚举里面的方法可以通过变量来调用");
}
`
```java
public class Test {
public static void main(String[] args) {
Singleton.INSTANCE.method();
}
}
使用枚举类型来实现的单例,不仅能避免多线程同步问题,而且能防止反序列化重新创建出一个新的对象。