单例模式
一. 什么是单例模式
因程序需要,有时我们只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计。
二. 单例模式的特点
-
单例模式只能有一个实例。
-
单例类必须创建自己的唯一实例。
-
单例类必须向其他对象提供这一实例。
单例模式的写法
饿汉式
饿汉方法的特点在于,提前创建了实例。一般是采用final static
方法保护起来。
// @TODO: 第一种单例模式模式:饿汉式(静态常量)(可用)
public class Sigleton1 {
private final static Sigleton1 INSTANCE = new Sigleton1();
// 构造方法。
private Sigleton1(){
System.out.println("Something");
}
public static Sigleton1 getInstance(){
return INSTANCE;
}
}
另外也可以利用静态代码块实现。
public class Sigleton2 {
private final static Sigleton2 INSTANCE;
static {
INSTANCE = new Sigleton2();
}
private Sigleton2(){
System.out.println("Something");
}
public static Sigleton2 getInstance(){
return INSTANCE;
}
}
懒汉式
懒汉式的特点在于将新建实例这一任务延迟到了获取实例的时候创建。
// 存在的问题,线程不安全,可能多个线程同时进入,新建出来不同的实例。
public class Sigleton3 {
private static Sigleton3 INSTANCE;
private Sigleton3(){
System.out.println("Something");
}
public static Sigleton3 getInstance(){
if (INSTANCE == null){
INSTANCE = new Sigleton3();
}
return INSTANCE;
}
}
------------------------------------------------
// @TODO: 懒汉模式2:利用synchronized的保护线程安全 (效率低下,不推荐)
// 无法继续进行并行,这里对整个方法进行了加锁,连并行得到实例都不可以了。
public class Sigleton4 {
private static Sigleton4 INSTANCE;
private Sigleton4(){
System.out.println("Something");
}
public synchronized static Sigleton4 getInstance(){
if (INSTANCE == null){
INSTANCE = new Sigleton4();
}
return INSTANCE;
}
}
-----------------------------------------------------
// @TODO: 懒汉模式3:synchronized对类对象的保护线程安全 !!注意当前方法是错误的
public class Sigleton5 {
private static Sigleton5 INSTANCE;
private Sigleton5(){
System.out.println("Something");
}
public static Sigleton5 getInstance(){
if (INSTANCE == null){
// 可能两个线程都卡在了这里。
synchronized(Sigleton5.class){
INSTANCE = new Sigleton5();
}
}
return INSTANCE;
}
}
双重检查【推荐】
优点:线程安全;延迟加载;效率高
- 为什么需要
volatile
修饰,因此新建对象这一个过程不是原子操作。防止进行重排序,保证完整的新建实例。可见性其实synchronized
已经保证了。synchronized
的近朱者赤原则。
// 懒汉模式4:synchronized对类对象的保护线程安全 ,并且采用了双重判断。
public class Sigleton6 {
private volatile static Sigleton6 INSTANCE;
private Sigleton6(){
System.out.println("Something");
}
public static Sigleton6 getInstance(){
if (INSTANCE == null){
synchronized(Sigleton6.class){
if (INSTANCE == null){
INSTANCE = new Sigleton6();
}
}
}
return INSTANCE;
}
}
静态内部类的方法
public class Sigleton7 {
private Sigleton7(){
System.out.println("Something");
}
// JVM保证了只会创建一个实例。
private static class SingletonInstance{
private static final Sigleton7 INSTANCE = new Sigleton7();
}
public static Sigleton7 getInstance(){
return SingletonInstance.INSTANCE;
}
}
枚举方法【推荐】
* @TODO: 枚举单例 最简单的方法
public enum Sigleton8 {
INSTANCE;
public void whatever(){
System.out.println("Something");
}
}
// 调用只需要 Sigleton.INSTANCE.whatever();
几种单例方法的对比
最佳方法:枚举类。写法最简单,线程安全有保证(枚举类的本质是final class
,继承枚举的父类,在父类中各个实例都是static
修饰的),避免反序列化破坏单例。
- 缺点
饿汉式:浪费资源,没必要提前加载。
懒汉式:写法复制,容易出现线程问题。