设计模式之单例模式
转载;https://blog.csdn.net/wangkai525and526/article/details/106995043
什么是单例模式?
单例模式(Singleton Pattern):是指确保一个类在任何情况都绝对只有一个实例,并且提供一个全局访问点。在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例。
单例模式的特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点
单例模式的写法:
- 饿汉式(可用)
- 懒汉式(不推荐)
- 线程安全-synchronized(可用)
- 双重检测-DoubleCheckLock(推荐)
- 内部类-InnerClass(推荐)
- 序列化-Serializable(可用)
- 容器单例-IOC(可用)
- 枚举-Enum(推荐)
- ThreadLocal
一、饿汉式
1、静态常量
public class SingletonTest {
private static final SingletonTest singleton = new SingletonTest();
private SingletonTest(){
System.out.println("无参构造方法执行");
}
public static SingletonTest getSingleton(){
return singleton;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println(SingletonTest.getSingleton());
}).start();
}
}
}
2、静态代码块
public class SingletonTest {
private static final SingletonTest singleton;
//静态代码块
static {
singleton = new SingletonTest();
}
private SingletonTest(){
System.out.println("无参构造方法执行");
}
public static SingletonTest getSingleton(){
return singleton;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println(SingletonTest.getSingleton());
}).start();
}
}
}
二、懒汉式
有线程调用getInstance方法的时候,开始进行初始化
优点:
1.开始不占用内存,只有调用了才初始化实例
缺点:
1.并发的时候,可能会出现多个实例。线程不安全,因为并发的时候,会出现创建多个实例的情况
public class SingletonTest2 {
private static SingletonTest2 singleton = null;
private SingletonTest2(){
System.out.println("无参构造方法执行");
}
public static SingletonTest2 getSingleton(){
if(singleton == null){
return singleton = new SingletonTest2();
}
return singleton;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println(SingletonTest2.getSingleton());
}).start();
}
}
}
三、单例模式之线程安全-synchronized(可用)
有线程调用getInstance方法的时候,开始进行初始化
优点:
1.开始不占用内存,只有调用了才初始化
2.线程安全,因为getInstance加上了锁,假如并发访问的话,拿到锁的线程,才能进入方法进行初始化,其他线程排队等待锁
缺点:
1.性能比较低,并发的时候,会出现其他线程等待的情况
2.每一次并发的时候,都会有锁的竞争情况
public class SingletonTest2 {
private static SingletonTest2 singleton = null;
private SingletonTest2(){
System.out.println("无参构造方法执行");
}
public static synchronized SingletonTest2 getSingleton(){
if(singleton == null){
return singleton = new SingletonTest2();
}
return singleton;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println(SingletonTest2.getSingleton());
}).start();
}
}
}
四、单例模式之DCL双重检测+volatile(推荐)
有线程调用getInstance方法的时候,开始进行初始化
优点:
1.开始不占用内存,只有调用了getInstance才初始化
2.第一次并发访问存在锁的竞争情况,后面任何一个一次并发都不会存在锁的竞争
缺点:
1.第一次并发的时候,有锁的竞争情况,存在线程等待
public class SingletonTest2 {
//加上 volatile 关键字,解决 instance = new SingletonTest2() 处的指令重排问题
private volatile static SingletonTest2 singleton = null;
private SingletonTest2(){
System.out.println("无参构造方法执行");
}
public static SingletonTest2 getSingleton(){
if(singleton == null){
synchronized (SingletonTest2.class){
if(singleton == null){
return singleton = new SingletonTest2();
}
}
}
return singleton;
}
public static void main(String[] args) {
for (int i = 0; i < 60; i++) {
new Thread(()->{
System.out.println(SingletonTest2.getSingleton());
}).start();
}
}
}
五、静态内部类(推荐)
JVM加载LazyInnerClassSingleton的时候,不会去加载内部类LazyHolder,只有调用getInstance方法的时候,JVM才会去加载LazyHolder,巧妙的避开了线程安全的问题
优点:
1.开始不会占用内存
2.兼顾了synchronized的性能问题
3.利用Java本身的语法特点,巧妙的避免了线程安全的问题
public class SingletonTest3 {
private SingletonTest3(){
System.out.println("无参构造方法执行");
}
private static class SingletonInstance{
private static final SingletonTest3 INIT_SINGLETON = new SingletonTest3();
}
public static SingletonTest3 getInstance(){
return SingletonInstance.INIT_SINGLETON;
}
public static void main(String[] args) {
for (int i = 0; i < 60; i++) {
new Thread(()->{
System.out.println(SingletonTest3.getInstance());
}).start();
}
}
}