一、前言:
转载需要注明出处: https://i.cnblogs.com/EditPosts.aspx?opt=1
单例模式其实很早之前就已经接触过了,但都是为了应付面试或者是为了装X。每过一段时间后,又有些模糊不清了,也仿佛从来没有项目中使用过,但最近终于有它的用武之地了。
二、单例模式的特点:
-
- 单例类只能有一个实例
- 单例类自己给自己创建一个实例,注意是唯一的一个实例
- 单例类必须给其他对象提供这唯一的实例
三、单例模式分类:
-
- 懒汉模式
懒汉模式即懒加载,第一次被调用前不实例化。对于创建实例代价大,且不一定会使用时,使用懒加载模式可以减少开销
-
- 饿汉模式
饿汉模式,在类加载的同时实例化对象
四、实现单例模式的各种方法:
-
- 线程不安全的懒汉模式
public class LazySingleton { private LazySingleton() { } private static LazySingleton single; public static LazySingleton getSinton(){ if (single == null ){ single = new LazySingleton(); } return single; } }
这种方法在单线程场景中,是安全的,但在多线程情况中,单例模式未必是单例模式,也就是存在线程安全问题。
-
- 同步方法下的多线程
public class LazySingleton { private LazySingleton() { } private static LazySingleton single; public static synchronized LazySingleton getSinton(){ if(single == null){ single = new LazySingleton(); } System.out.println(Thread.currentThread().getName()+": "+single); return single; } }
使用sychronized修饰获取实例对象的方法,这种方法可以保证线程安全,但效率不高,每个线程都需要来获取 对象锁。
-
- 同步代码块的懒汉模式
public class LazySingleton { private LazySingleton() { } private static LazySingleton single; public static LazySingleton getSinton() { if (single == null) { synchronized (LazySingleton.class) { single = new LazySingleton(); } } System.out.println(Thread.currentThread().getName() + ": " + single); return single; } }
由同步方法想到同步代码块,但实际上,这种方法并不是线程安全。
解释:两个线程A、B,A线程判断single是空的 ,当获取到锁后;B线程开始判断,发现single是空的;这时A线程又快人一步,创建了一个对象;线程B来了,获取到锁,也创建了一个对象。
-
- 双重检查的懒汉
public class LazySingleton { private LazySingleton() { } private static LazySingleton single; public static LazySingleton getSinton() { if (single == null) { synchronized (LazySingleton.class) { if(single== null){ single = new LazySingleton(); } } } System.out.println(Thread.currentThread().getName() + ": " + single); return single; } }
针对同步代码块的懒汉,双重检查的懒汉可以说是最佳的,既保证了线程安全,又不需要每次都获取锁资源。(推荐双重检查懒汉)
-
- 静态常量饿汉
public class HungrySingleTon { private HungrySingleTon(){ } private final static HungrySingleTon single = new HungrySingleTon(); public static HungrySingleTon getInstance(){ return single; } }
无线程安全问题,缺点是类装在完成实例化,若一直未使用,会造成资源浪费。
-
- 静态代码块饿汉模式
public class HungrySingleTon { private HungrySingleTon() { } private static HungrySingleTon single; static { single = new HungrySingleTon(); } public static HungrySingleTon getInstance() { return single; } }
线程安全,与常量饿汉模式一样,若一直未被使用,会造成资源浪费。
-
- 静态内部类
public class HungrySingleTon { private HungrySingleTon() { } public static HungrySingleTon getInstance() { return InnerClass.single; } private static class InnerClass { private static final HungrySingleTon single = new HungrySingleTon(); } }
只有调用getInstance()时才会加载静态内部类。无线程安全问题,也实现了懒加载。