单例模式
什么是单例模式
单例模式就是在全局只有一个实例。
写法介绍
单例模式分为:懒汉式、饿汉式
饿汉式(hungry):在启动项目的时候就创建实例,而不是在调用的时候加载实例(线程安全、空间换时间、造成一定的性能浪费)
懒汉式(Lazy):在调用的时候在进行实例化,线程不安全、时间换空间
饿汉式写法
1 2 3 4 5 6 7 8 9 10
| public class Hungry{ private Hungry(){ } private Hungry instance = new Hungru(); public static Hungry getInstance(){ return instance; } }
|
优点:线程安全
缺点:资源浪费
懒汉汉式写法
1、基础版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Lazy{ private Lazy(){ } private static Lazy instance = null; public static Lazy getInstance(){ if(instance == null){ instance = new Lazy(); return instance; } return instance; } }
|
优点:节省空间,用到的时候在去创建实例对象
缺点:线程不安全
2、同步锁版本
1 2 3 4 5 6 7 8 9 10 11
| public class Lazy{ private Lazy(){} private static Lazy instance = null; public synchronized static Lazy getInstance(){ if(instance==null){ instance = new Lazy(); return instance; } return instance; } }
|
优点:线程安全
缺点:不管是不是已经初始化成功都需要同步访问方法,有严重的性能问题
3、双重检测锁
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Lazy{ private Lazy(){} private static Lazy instance = null; public static Lazy getInstance(){ if(instance==null){ synchronize(Lzay.class){ instance = new Lazy(); return instance; } } return instance; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Lazy{ private Lazy(){} private static Lazy instance = null; public static Lazy getInstance(){ if(instance==null){ synchronize(Lzay.class){ if(instance==null){ instance = new Lazy(); return instance; } } } return instance; } }
|
优点:优化了同步版本的严重性能消耗
4、静态内部类创建
静态内部类也称作Singleton Holder, 也就是单持有者模式, 是线程安全的, 也是懒惰模式的变形.
JVM加载类的时候, 有这么几个步骤:
①加载 -> ②验证 -> ③准备 -> ④解析 -> ⑤初始化
需要注意的是: JVM在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类(SingletonHolder)的属性/方法被调用时才会被加载, 并初始化其静态属性(instance).
1 2 3 4 5 6 7 8 9
| public class Lazy{ private Lazy(){} public static Lazy getInstance(){ return InLazy.instance; } private static class InLazy{ private static Lazy instance = new Lazy(); } }
|
- 在初始化过程并不会加载内部类,所以当调用的时候才会加载内部类。
- 没有加锁、线程安全、用时在加载,并发高。
5、容器式单例
- 当单例很多的时候需要容器来管理单例,这个时候就可以用Map中线程安全的
`ConcurrentHashMap来进行容器单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Lazy{ private Lazy(){} private static Map<String,Object> singletonMap = new ConcurrnHashMap<>(); public static Object getInStance(Class clazz) throws Exception{ String className = clazz.getName(); if (!singletonMap.containsKey(className)) { Object instance = Class.forName(className).newInstance(); singletonMap.put(className, instance); return instance; } return singletonMap.get(className); } public static void main(String[] args) throws Exception { SafetyDangerLibrary instance1 = (SafetyDangerLibrary)ContainerSingleton.getInstance(SafetyDangerLibrary.class); SafetyDangerLibrary instance2 = (SafetyDangerLibrary)ContainerSingleton.getInstance(SafetyDangerLibrary.class); System.out.println(instance1 == instance2); }
}
|
6、ThreadLocal单例
不保证整个应用全局唯一,但保证线程内部全局唯一,以空间换时间,且线程安全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class ThreadLocalSingleton { private ThreadLocalSingleton(){} private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = ThreadLocal.withInitial(() -> new ThreadLocalSingleton()); public static ThreadLocalSingleton getInstance(){ return threadLocalInstance.get(); } public static void main(String[] args) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance()); System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance()); }).start(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance()); System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance()); }).start();
} }
|
参考: