java实现单例模式
1.最简单的方式:
  1. public class Singleton {
  2. private static final Singleton singleton = new Singleton();
  3. private Singleton(){
  4. }
  5. public Singleton getInstance(){
  6. return singleton;
  7. }
  8. }
分析:
    优点:简单明了,预防多线程问题
    缺点:项目启动时就要初始化,如果初始化很复杂,很可能造成启动超时。如果实例一直没有被用到,则造成资源浪费。
改进方式如下:
2.懒加载:
    调用getInstance方法的时候,再去初始化对象。
  1. public class Singleton {
  2. private static Singleton singleton;
  3. private Singleton(){}
  4. public Singleton getInstance(){
  5. if(singleton == null){
  6. singleton = new Singleton();
  7. }
  8. return singleton;
  9. }
  10. }
分析:
    优点:思路简单,而且在需要单例对象的时候再去初始化,达到延迟加载的效果
    缺点:多线程情况下容易出问题:两个线程同时走到if(singleton==null)的时候,就可能会导致初始化两次。
改进方式如下:
3.加锁:
    在if(getInstance == null)这段的时候加上锁,这样就不会有两个线程同时初始化对象啦。
  1. public class Singleton {
  2. private static Singleton singleton;
  3. private SingletonLock(){}
  4. public synchronized Singleton getInstance(){
  5. if(singleton == null){
  6. singleton = new Singleton();
  7. }
  8. return singleton;
  9. }
  10. }

分析:
    优点:懒加载有了,线程同步也有了,总算可以长舒一口气了。
    缺点:每次调用时都会进行线程同步,如果我调用的频率很高,这也太浪费性能了。
改进方式如下:
3.双重检查锁:
    先判断是否为空,如果如果为空,再加锁进行初始化。
  1. public class Singleton {
  2. private static Singleton singleton;
  3. private Singleton(){}
  4. public Singleton getInstance(){
  5. if(singleton == null){
  6. synchronized (Singleton.class){
  7. if(singleton == null){
  8. singleton = new Singleton();
  9. }
  10. }
  11. }
  12. return singleton;
  13. }
  14. }
分析:
    优点:懒加载和同步依然有,而且在大频率调用的情况下,不会出现性能问题。这下总完美了吧
    缺点:有个极端情况,如果初始化的时候要做很多操作(如下),会不会出现线程A正在“标记1“时进行赋值,,线程B在”标记2“处发现singleton不为null了(内存空间已经分配),程序就直接把没初始化完毕的singleton返回给线程B了?
  1. public class Singleton {
  2. private static Singleton singleton;
  3. private String fileName; //ftp上要打印的文件名称
  4. private Singleton(){
  5. fileName = getFTPFile(); // 标记1
  6. }
  7. private String getFTPFile() {
  8. String remoteFileName = "";// 很艰难地连接到ftp上,然后拿到文件
  9. return remoteFileName;
  10. }
  11. public Singleton getInstance(){
  12. if(singleton == null){ // 标记2
  13. synchronized (Singleton.class){
  14. if(singleton == null){
  15. singleton = new Singleton();
  16. }
  17. }
  18. }
  19. return singleton;
  20. }
  21. }
改进如下:
  1. private static volatile SingletonDoubleCheck singleton;
分析:
    优点:用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。就是说,当发现其它线程在对这个变量正在写的时候,当前线程就不忙着读它。这样就能完美解决了上面的问题。
    缺点:volatile也需要耗费性能的好伐?而且volatile是JDK1.5之后才有的好不啦?
改进如下:
    思考:
        1.使用普通类的方式实现单例都有两个致命问题:Java反序列化时都是新生成一个对象的;使用反射是可以直接调用private的构造方法。因此,不能从根本上保证只生成一个实例.(话说谁那么无聊啊,费劲心机就是为了多序列化一个实例??)
        2.如果只是想单纯实现纯单例,而没有其他复杂的要求的话,没有必要使用双重检查锁这么复杂的方式。
  1. public enum SingletonEnum {
  2. singleton;
  3. public void doSomething(){}
  4. }
分析:
    优点:实现简单;jdk底层抗序列化;jdk底层类加载机制保证线程安全;底层是用abstract修饰,无法实例化,所以也无法使用反射来初始化。
    缺点:不是懒加载;jdk1.5之后才支持;失去了类的特性,有时不太好用。
改进如下:
  1. public class Singleton {
  2. private static class SingletonClassInstance {
  3. private static final Singleton instance = new Singleton();
  4. }
  5. public static Singleton getInstance() {
  6. return SingletonClassInstance.instance;
  7. }
  8. private Singleton() {}
  9. }
分析:
    优点:懒加载;线程安全;任何jdk版本都可用
    缺点:额外生成一个永生代的类,增加程序的复杂性;

评价:
    没有最好的实现方式,只有最适合自己的方式。一个小小的单例,就包含了这么多的知识点,学无止境哇。



来自为知笔记(Wiz)


posted on 2015-10-30 22:07  bishion  阅读(187)  评论(0编辑  收藏  举报