单例模式:一般是指在运行的JVM中只有一个实例存在。通过使用 private的构造函数确保了在一个应用中只能产生一个类的实例,
并且实例是在单例类Singleton中自己实例化的。
主要分为 懒汉模式和饿汉模式
两者结构:
均是 一个静态的实例[ 静态为类对象]
私有的构造方法。防止外部直接常见对象
静态方法:返回类中的静态实例对象
懒汉,饿汉主要是创建实例的时机不一样。
饿汉模式:是在类加载后就会对类的静态对象进行初始化。
private static final Singleton singleton = new Singleton(); //私有化构造函数 private Singleton() { } //给出一个公共的静态方法返回一个单一实例 public static Singleton getInstance() { return singleton; }
这个方法却牺牲了Lazy的特性。单例类加载的时候就实例化了。如注释所述:非懒加载,如果构造的单例很大,构造完又迟迟不使用,会导致资源浪费。
懒汉模式:是在方法调用的时候才会创建实例。
private static Singleton singleton; //私有化构造函数 private Singleton() { } //给出一个公共的静态方法返回一个单一实例 public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); } return singleton; }
单例模式推荐的写法
private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton() { } public static Singleton getInstance() { return SingletonHolder.INSTANCE; }
static的成员变量只在类加载的时候初始化一次,且类加载是线程安全的。所以这个方法实现的单例是线程安全的。
内部类SingletonHolder只有在getInstance()方法第一次调用的时候才会被加载(实现了lazy),而且类的加载过程是线程安全的。内部类加载的时候实例化一次instance。全局也就一个单例
模拟多线程环境下。懒汉单例模式的缺陷,可能导致在jvm中产生多个实例。
public static void main(String[] args) throws Exception { int num = 20; final CyclicBarrier start = new CyclicBarrier(num); final CountDownLatch end = new CountDownLatch(num); final Set<String> set = Collections.synchronizedSet(new HashSet<String>()); ExecutorService executorService = Executors.newFixedThreadPool(num); for (int i = 0; i < num; i++) { executorService.execute(new SingletonRunnable(i, start, end, set)); } end.await(); System.out.println("------并发情况下我们取到的实例------"); for (String instance : set) { System.out.println(instance); } executorService.shutdown(); } public static class SingletonRunnable implements Runnable { private CountDownLatch countDownLatch; private CyclicBarrier start; private Integer id; private Set<String> set; SingletonRunnable(Integer id, CyclicBarrier start, CountDownLatch countDownLatch, Set<String> set) { this.countDownLatch = countDownLatch; this.start = start; this.id = id; this.set = set; } @Override public void run() { try { System.out.println("准备构造------------" + id); start.await(); System.out.println("开始构造" + id); set.add(Singleton.getInstance().toString()); } catch (Exception e) { } countDownLatch.countDown(); } }
与Spring容器的单例的区别:
Spring中说的单例是相对于容器的,既在ApplicationContext中是单例的。而平常说的单例是相对于JVM的。另一个JVM可以有多个Spring容器,而且Spring中的单例也只是按bean的id来区分的