单例模式的那些事

写在前面

  单例Singleton设计模式, 老生常谈的一个设计模式。但你真的用对了么? 用的姿势很重要!

1.概念解释

  单例顾名思义就是只产生一个实例对象。那怎样保证单一呢?把目标类提供给外部创建实例对象的能力收回,即构造函数设为私有,然后内部提供一个生成实例静态方法。设计成单例模式, 有各种各样的实现方式。

2.单例模式的设计

2.1 饿汉式单例

  饿汉式单例是指在方法调用前,实例就已经创建好了。

-实现代码:

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {

        return instance;
    }
}

-验证高并发下的执行情况

public class SingletonClient {

    public static void main(String[] args) throws InterruptedException {

        Thread[] threads = new Thread[10];

        for (int i = 0; i < 10; i++) {

            final Singleton instance = Singleton.getInstance();

            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(instance.hashCode());
                }
            });
        }

        for (Thread thread : threads) {
            thread.start();
        }
    }
}
  • 结果
165827088
165827088
165827088
165827088
165827088
165827088
165827088
165827088
165827088
165827088

2.2 懒汉式单例

懒汉式单例是指在方法调用获取实例时才创建实例,因为相对饿汉式显得“不急迫”,所以被叫做“懒汉模式”。

1)单线程的懒汉式单例 -- 初学者最容易犯错的写法.

public class Singleton {

    private static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {

        if (instance == null) {

            try {
                // 刻意模仿多线程访问下创建实例不是单一的情况
                Thread.sleep(100); 
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            instance = new Singleton();
        }
        return instance;
    }
}


上面创建单例在单线程下是木问题的。但在多线程访问下就会有问题啦。
如何改进呢?来,再往下看

2)线程安全的懒汉式单例.

-同步方法锁定
  同步方法效率很低, 它把真个方法都加了锁, 在高并发下, 只允许一个线程进入到该方法

public class Singleton {

    private static Singleton instance = null;

    private Singleton() {

    }

    public synchronized static Singleton getInstance() {

        if (instance == null) {

            instance = new Singleton();
        }
        return instance;
    }
}

-同步代码块锁定
  同步代码块效率也很低,但比同步方法效率高点, 它把高并发的代码块加了锁, 在高并发下, 只允许一个线程进入到该代码块。

public class Singleton {

    private static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {

        try {

            synchronized (Singleton.class) {

                if (instance == null) {

                    Thread.sleep(100);

                    instance = new Singleton();
                }
            }
        } catch (Exception e) {

        }
        return instance;
    }
}

-双检查锁机制(推荐
这种方式不仅能保证线性安全, 还能提高效率。因为volatile关键字保证多线程间的可见性;在同步块中使用二次检查,以保证其不被重复实例化。集合其二者,这种实现方式既保证了其高效性,也保证了其线程安全性。

public class Singleton {

    // volatile 关键词修饰
    volatile private static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {

        try {

            if (instance == null) {

                synchronized (Singleton.class) {

                    Thread.sleep(100);

                    instance = new Singleton();
                }
            }
        } catch (Exception e) {

        }
        return instance;
    }
}

3)使用静态内置类实现单例模式.

public class Singleton {

    private Singleton() {

    }

    private static class SingletonHandler {

        private static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {

        return SingletonHandler.instance;
    }
}

4)使用静态代码块实现单例模式

public class Singleton {

    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    private Singleton() {

    }


    public static Singleton getInstance() {

        return instance;
    }
}

写在后面

新博客

原文地址:https://www.cnblogs.com/chenmo-xpw/p/7056239.html