一 简介
单例模式,对于一个类只有一个实例化对象。
必要点:
私有化 构造方法,不让new
编写类方法 暴露单例对象
私有化单例对象,只提供给内部使用
二 考虑到并发条件下的情况下
实现方式主要有如下四种
第一种,饿汉模式随着类加载,而实例化。类只会实例化一次,所以不会有并发的问题
/** * @program: test * @description: 单例 四种实现方式 第一饿汉式 类加载就初始化 * 第二 懒汉式 方法加锁 * 第三 双重效验 * 第四 静态内部类 * @author: * @create: 2019-07-09 19:46 */ public class Singleton { private Singleton() { } private static Singleton singleton = new Singleton(); public static Singleton getSingleton() { return singleton; } }
第二种 对方法封锁,保证临界资源安全,好处:想加载时,才加载
public class Singleton1 { private static Singleton1 singleton1; private Singleton1() { } public synchronized static Singleton1 getSingleton() { if(singleton1==null) singleton1 = new Singleton1(); //只有第一次进入方法体的时候 创建对象 return singleton1; } }
对比的是一种,线程不安全的。
/** * @program: test * @description: 不加限制的懒加载 * @author: * @create: 2019-07-09 20:09 */ public class Singleton4 { private static Singleton4 singleton4; private Singleton4() { } public static Singleton4 getSingleton() { if (singleton4 == null) { singleton4 = new Singleton4(); } return singleton4; } }
第三种 方法:双检验
public class Singleton3 { private static volatile Singleton3 singleton3; private Singleton3() { } public static Singleton3 getSingleton() { if (singleton3 == null) { synchronized (Singleton3.class) { if (singleton3 == null) { singleton3 = new Singleton3(); } } //同样 保证第一次 进入方法体 进行初始化 优化第二种情况 } return singleton3; } }
第四种方法:利用内部类的特性,内部类可以访问外部类的变量;内部类同样也只会加载一次
public class Singleton2 { private static Singleton2 singleton2; private Singleton2() { } private static class SingletonPv { private static Singleton2 singleton2 = new Singleton2(); } private Singleton2 getSingleton() { return SingletonPv.singleton2; } }
最后是测试,让100个线程 同时去获取单例对象,四种方法 都没问题;而没加并发限制,很容易出现100个线程获得的对象不相同的情况
public class Main { public static void main(String[] args) { final CountDownLatch countDownLatch = new CountDownLatch(100); for (int i = 0; i < 100; i++) { Thread thread = new Thread(new Runnable() { public void run() { try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( Singleton3.getSingleton()+Thread.currentThread().getName()); } }); thread.start(); countDownLatch.countDown(); } } }