1.概述:
所谓单例,就是整个程序有且仅有一个实例.该类负责创建自己的对象,同时确保只有一个对象被创建,常用于工具类的实现和创建对象需要消耗资源
特点:
构造器私有
持有自己的属性
对外提供获取实例的静态方法
2.创建单例的5种方式
- 饿汉模式
- 懒汉模式
- 静态内部类
- 枚举
1.饿汉模式:
当程序启动或类被加载的时候,实例就就被创建。
public class SingleBean{
//构造私有
private SingleBean(){}
private static SingleBean singleBean=new SingleBean();
public static SingleBean getInstance(){
return singleBean;
}
}
2懒汉模式
—优点:单例对象在需要时,才会去构造
—缺点:线程不安全的,多个线程调用,会产生多个单例对象
public class SingleBean{
//构造私有
private SingleBean(){}
private static SingleBean singleBean=null;
public static SingleBean getInstance(){
if(null==singleton){
singleBean=new Singleton();
}
return singleBean;
}
2.1懒汉模式(加双重锁+双重判断)
–优点:可以解决线程安全问题,效率高
public class SingleBean{
//构造私有
private SingleBean(){}
private static volatile SingleBean singleBean=null;
public static SingleBean getInstance(){
//懒汉式初始化
if(singleBean!=null){
return singleBean;
}
synchronized(SingBean.class){
if(singleBean!=null){
return singleBean;
}
singleBean =new SingleBean();
}
return singleBean;
}
}
解释:
由于singleton=new Singleton()
对象的创建在JVM中可能会进行重排序,在多线程访问下存在风险,使用volatile
修饰signleton
实例变量有效,解决该问题。
1.第一个判断,避免不要的实例。解决多线程情况下,不为空,直接返回,不需要再判断同步锁,提高性能
2.第二个判断,避免同步。在多线程情况下,一条线程a在执行new对象操作,其他线程在锁外等待,不加判断的话,则线程a执行完new之后,会产出多个单例对象
3.静态内部类
java的静态内部类的加载时在使用在该静态内部类的时候才去加载,是线程安全的
- 优点:实现简单,懒加载,线程安全
- 缺点:增加一个静态内部类,apk文件增大
public class SingleBean{
//构造私有
private SingleBean(){}
private static class InnerClass{
private static SingleBean singleBean;
static{
singleBean=new SingleBean();
}
}
public static SingleBean getInstance(){
return InnerClass.singleBean;
}
}
4.枚举
众所周知,单例模式是创建型模式,都会新建一个实例。那么一个重要的问题就是反序列化。当实例被写入到文件到反序列化成实例时,我们需要重写readResolve方法,以让实例唯一。
- 优点:使用枚举是线程安全的,不用担心序列化和反射问题
- 缺点:枚举占用内存多一点,。但枚举实例在日常开发是很少使用的,就是很简单以导致可读性较差。
public enum EnumSingleton{
SINGLEBEAN;
//doSomething 该实例支持的行为
//可以省略此方法,通过Singleton.SINGLEBEAN进行操作
public static Singleton getInstance() {
return Singleton.SINGLEBEAN;
}
}
5.CAS 自旋锁的方式创建单例
//基于CAS的单例模式
private static final AtomicReference<SingleBean> INSTANCE = new AtomicReference<SingleBean>();
public static final SingleBean getInstance() {
for (; ; ) {
SingleBean current = INSTANCE.get();
if (current != null) {
return current;
}
current = new SingleBean();//高并发的场景下,可能会创建多个对象,但是最终只有一个会被使用,其它的会被丢弃
System.out.println(current);
if (INSTANCE.compareAndSet(null, current)) {
return current;
}
}
}