JAVA 单例模式总结
1.单列模式
1.手写单例模式 2.线程安全的单例模式
什么是单例模式?
单例类在整个程序中只能有一个实例,这个类负责创建自己的对象,并确保只有一个对象被创建。
在什么情况下使用单例?一般全局使用的类写成单例,会消耗许多系统资源的类写成单例(数据库连接池、工厂类、数据源、sping bean的作用域等)(创建销毁都需要消耗许多系统资源)。
代码实现要点 a) 私有化构造方法 b) 持有该类的属性(创建对象) c) 对外提供获取对象的静态方法
1)饿汉式是典型的单例模式(没有加锁),通过JAVA的类加载机制实现。类加载机制默认是线程安全的,类加载成功,静态属性被初始化,即对象初始化是无延迟的。 饿汉式:线程安全、反射不安全、反序列化不安全(添加readResolve()方法可以保证反序列化安全)
/*
饿汉式
*/
public class Singleton1
{
private Singleton1()//私有化构造方法
{}
private static Singleton1 instance= new Singleton1();//当前类的属性(静态可以全局应用)
public static Singleton1 getInstance()//对外提供获取对象的静态方法
{
return instance;
}
}
2)登记式:线程安全、防止反射攻击、反序列化不安全(添加readResolve()方法可以保证反序列化安全)
延迟初始化
/*
登记式
*/
public class Singleton2
{
private Singleton2()//私有化构造方法
{
if(SingletonHolder.instance != null)
{
throw new IllegalStateException(); //若静态内部类方法不为空则抛出状态异常
}
}
private static class SingletonHolder{
private static Singleton2 instance = new Singleton2(); //静态内部类
}
public static Singleton2 getInstance()
{
return SingletonHolder.instance; //对外提供获取对象的静态方法
}
}
3)枚举式:线程安全、反射安全(没有构造方法)、反序列化安全
/*
枚举式
*/
public enum Singleton3
{
INSTANCE //枚举中的对象实现方法
{
protected void doSomething()
{
System.out.println("doSomething");
}
};
protected abstract void doSomething();
}
4)懒汉式
/*
懒汉式 同步方法
*/
public class Singleton4
{
private Singleton4() //私有化构造方法
{}
private static Singleton4 instance = null; //持有当前类的属性
public static synchronized Singleton4 getInstance() //对外提供获取对象的静态方法
{
if(instance == null)
{
instance =new Singleton4();
}
return instance;
}
}
/*
懒汉式 同步代码块
*/
public class Singleton4
{
private Singleton4() //私有化构造方法
{
}
private static Singleton4 instance = null; //持有当前类的属性
public static Singleton4 getInstance() //对外提供获取对象的静态方法
{
synchronized (Singleton4.class)
{
if (instance == null)
{
instance = new Singleton4();
}
return instance;
}
}
}
5)双重检锁
/*
双重检锁
*/
public class Singleton5
{
private Singleton5() //私有化构造方法
{
}
//双检锁加volatile关键字解决指令重排
private static volatile Singleton5 instance = null; //持有当前类的属性
public static Singleton5 getInstance() //对外提供获取对象的静态方法
{
if (instance == null)//双重检锁 效率高
{
synchronized (Singleton5.class)
{
if (instance == null)
{
instance = new Singleton5();
}
}
}
return instance;
}
}
/*instance=new Singleton5()会执行如下操作:
(1)分配对象内存空间
(2)初始化对象
(3)instance指向(1)中分配的空间
在某些编译器上,可能会出现指令重排:
(1)分配对象内存空间
(2)instance指向(1)中分配的空间(但此时对象没有初始化)
(3)初始化对象
解决方法:在当前对象前加volatile关键字,确保不出现指令重排
*/
6)ThreadLocal
ThreadLocal:不加锁,以空间换时间,为每个线程提供变量的独立副本
/*
TreadLocal 只能保证一个线程内是单例的(即一个线程同时获取多个对象是同一对象) 不能保证多个线程间是单例的
*/
public class Singleton6
{
private Singleton6()
{
}
private static Singleton6 instance = null; //延时创建对象
private static final ThreadLocal<Singleton6> thread = new ThreadLocal<Singleton6>()
{
@Override
protected Singleton6 initialValue() //重写初始化方法
{
return new Singleton6();
}
};
public static Singleton6 getInstance()
{
return thread.get();
}
}
7)CAS( Compare-and-Swap)
无锁的乐观策略:假设进程访问资源不会产生冲突,如果出现冲突就重试当前的操作
/*
CAS
*/
public class Singleton7
{
private Singleton7()
{
}
//原子类的封装
private static final AtomicReference<Singleton7> instance = new AtomicReference<Singleton7>();
public static final Singleton7 getInstance()
{
for (; ; )//线程重试
{
Singleton7 current = instance.get();//拿到原子类引用instance指向的对象current
if (current != null)
{
return current; //判断current指向对象是否为null,非null返回
}
current = new Singleton7(); //为null就创建对象 (延时加载)
if (instance.compareAndSet(null, current))//调用原子类的compareAndSet方法进行判断
{
return current; //如果此时instance指向是null,则用current替换instance
}
}
}
}