高性能高质量代码:单例模式

92 阅读3分钟

书写代码必须符合高质量高性能要求,这也是能够在视觉上和其他程序员拉开差距的技能,同时也是一个优秀程序员的基本要求。

何为高质量: 代码具备可维护性,可读性,可扩展性,灵活性,简洁性,可复用性, 可测试性。

何为高性能: 代码能尽可能的提高处理效率。

如何写高质量高性能代码: 首先要做的就是精通设计模式,设计原则,掌握各种算法以及了解硬件底层相关内容。

今天我们来说一说单例模式。

1单例模式

饿汉式

public class IdGenerator {
        private AtomicLong id = new AtomicLong(0);
        private static final IdGenerator instance = new IdGenerator();

        private IdGenerator() {
        }

        public static IdGenerator getInstance() {
            return instance;
        }

        public long getId() {
            return id.incrementAndGet();

        }
    }

懒汉式

public class IdGenerator {
        private AtomicLong id = new AtomicLong(0);
        private static IdGenerator instance;

        private IdGenerator() {
        }

        public static synchronized IdGenerator getInstance() {
            if (instance == null) {
                instance = new IdGenerator();
            }
            return instance;
        }

        public long getId() {
            return id.incrementAndGet();
        }
    }

双重检测

public class IdGenerator {
        private AtomicLong id = new AtomicLong(0);
        private static IdGenerator instance;

        private IdGenerator() {
        }

        public static IdGenerator getInstance() {

            if (instance == null) {
                synchronized (IdGenerator.class) {
                    // 此处为类级别的锁  
                    if (instance == null) {
                        instance = new IdGenerator();
                    }
                }
            }
            return instance;
        }

        public long getId() {
            return id.incrementAndGet();
        }
    }

网上有人说,这种实现方式有些问题。因为指令重排序,可能会导致IdGenerator对象被new出来,并且赋值给instance之后,还没来得及初始化(执行构造函数中的代码逻辑),就被另一个线程使用了。 要解决这个问题,我们需要给instance成员变量加上volatile关键字,禁止指令重排序才行。实际上,只有很低版本的Java才会有这个问题。我们现在用的高版本的Java已经在JDK内部实现中解决了这个问题(解决的方法很简单,只要把对象new操作和初始化操作设计为原子操作,就自然能禁止重排序)。

静态内部类

 public class IdGenerator {
                        private AtomicLong id = new AtomicLong(0);

                        private IdGenerator() {
                        }

                        private static class SingletonHolder {
                            private static final IdGenerator instance = new IdGenerator();
                        }

                        public static IdGenerator getInstance() {
                            return SingletonHolder.instance;
                        }

                        public long getId() {
                            return id.incrementAndGet();
                        }
                    }

枚举

public enum IdGenerator {
                        INSTANCE;
                        private AtomicLong id = new AtomicLong(0);

                        public long getId() {
                            return id.incrementAndGet();
                        }
                    }

关于单利模式的实现,大体就是上面这几种,具体来说一下:

共同特征:

  • 构造函数必须使用private修饰,避免外部创建;
  • 都需要考虑安全问题;
  • 是否支持延迟加载;
  • 考虑性能问题;

不同点:

  • 饿汉式实现简单,在类加载的时候静态实例就已经创建好了,因此,创建是安全的,但是不支持延迟加载,就有可能导致资源占用问题。
  • 懒汉式略微复杂,支持延迟加载,但是实现中加了一把大锁,因此并发性低,性能低下。不适合频繁调用的场景。
  • 双重检测的单例支持延迟加载,支持高并发,安全可靠,低版本的jdk可能创建单例对象时,可能会存在重排序的导出错,因此,最好是做下修改
 private static IdGenerator instance
 修改为
 private static volatile IdGenerator instance
  • 静态内部类的实现,因为jvm类加载本身就是默认延迟加载,此内部类只有在使用的时候才会被加载,因此支持延迟加载,因为是静态类,对象的创建是在类加载的时候,因此可以保证安全可靠,应该是一个具备延迟加载的饿汉式单例。
  • 枚举的方式实现单例,这种实现方式通过java枚举型本身的特性,保证了实例创建的线程安全性和唯一性

如果需要带注释的spring源码或者了解更多行业技能请关注微信公众号 码农本农