单例模式笔记

懒汉模式
class Singleton2 {
    private static Singleton2 instances = new Singleton2();

    public static synchronized Singleton2 getInstances() {
        return instances;
    }

    private Singleton2() {
    }
}

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。


饿汉模式
//饿汉
class Singleton2 {
    private static Singleton2 instances = new Singleton2();

    public static synchronized Singleton2 getInstances() {
        return instances;
    }

    private Singleton2() {
    }
}
//这种方式基于classloder机制避免了多线程的同步问题,instance在类装载时就实例化.

目前java单例是指一个虚拟机的范围,因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的ClassLoader装载饿汉式实现单例类的时候就会创建一个类的实例。这就意味着一个虚拟机里面有很多ClassLoader,而这些classloader都能装载某个类的话,就算这个类是单例,也能产生很多实例。


静态内部类
//静态内部类
class Singleton4 {
    public static int a = 10;

    private Singleton4() {
        System.out.println("Singleton4 Constructor");
    }

    public static final Singleton4 getInstance() {
        System.out.println("Singleton4.getInstance()");
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final Singleton4 INSTANCE = new Singleton4();

        //延迟加载
        //内部类默认不会被classLoader加载, 只有调用Singleton4.getInstance时才会加载.
        public SingletonHolder() {
            System.out.println("SingletonHolder constructor");
        }
        static {
            System.out.println("SingletonHolder static container");
        }
    }
}

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载!


双重检查
//双重检查
class Singleton3 {
    private static volatile Singleton3 instances = null;

    public static Singleton3 getInstances() {
        if (instances != null) {
            return instances;
        } else {
            synchronized (Singleton3.class) {
                instances = new Singleton3();
            }
        }
        return instances;
    }

    private Singleton3() {
    }
}

这样方式实现线程安全地创建实例,而又不会对性能造成太大影响。它只是第一次创建实例的时候同步,以后就不需要同步了。


备注:

由于volatile关键字屏蔽了虚拟机中一些必要的代码优化,所以运行效率并不是很高,因此建议没有特别的需要不要使用。双重检验锁方式的单例不建议大量使用,根据情况决定。

volatile笔记:
volatile确保所有线程看见相同的值,不允许保持volatile变量到本地副本.
valatile只是在线程和内存之间同步变量的值,速度快,代价地.,而同步方法则需要线程所有变量与内存之间提供同步,代价更高
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
volatile只保证 并发的可见性,不保证操作的原子性(如自增操作)
volatile会保证有序性. volatile语句前及后的语句可能会重排, 但volatile前的语句不能重排的v之后.[原理是汇编是加入lock前缀,相当于内存栅栏,内存栅栏钱的数据会立即存入主存,]


注意问题:

1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

对第一个问题修复的办法是:

private static Class getClass(String classname)    
                                         throws ClassNotFoundException {   
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();   

      if(classLoader == null)   
         classLoader = Singleton.class.getClassLoader();   

      return (classLoader.loadClass(classname));   
   }   
}

对第二个问题修复的办法是:

public class Singleton implements java.io.Serializable {   
   public static Singleton INSTANCE = new Singleton();   

   protected Singleton() {   

   }   
   private Object readResolve() {   
            return INSTANCE;   
      }  
}

主要内容转自:https://my.oschina.net/dyyweb/blog/609021

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值