前言:
👏作者简介:我是笑霸final,一名热爱技术的在校学生。
📝个人主页:个人主页1 || 笑霸final的主页2
📕系列专栏:计算机基础专栏
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏
目录
- 一、单例模式定义
- 特点
- 二、单列模式分类
- 三、饿汉式【可用】
- 3.1饿汉式(静态常量)
- 3.2饿汉式(静态代码块)
- 四、懒汉式【不可用】
- 4.1懒汉式(线程不安全)【不可用】
- 4.2懒汉式(线程安全,同步方法)【不可用】
- 4.3 懒汉式(线程安全 ,同步代码块)【不可用】
- 五、双重检查
- volatile
- 六、静态内部类
- 七、枚举
- 八、适用场景:
一、单例模式定义
单例模式(Singleton Pattern):属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例
特点
- a、单例类只能有一个实例。
- b、单例类必须自己创建自己的唯一实例。
- c、单例类必须给所有其他对象提供这一实例。
- 单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。
二、单列模式分类
设计模式代码仓库
-
1.饿汉式(静态常量)【可用】
-
2.饿汉式(静态代码块)【可用】
饿汉式都可能造成内存的浪费
-
3.懒汉式(线程不安全)【不可用】
-
4.懒汉式(线程安全,同步方法)【不可用】
-
5.懒汉式(线程安全,同步代码块)【不可用】[
-
6.双重检查【推荐使用】
-
7.静态内部类【推荐使用】
-
8.枚举【推荐使用】[枚举]
三、饿汉式【可用】
下面两种方式都可用 但是
都可能造成内存的浪费
3.1饿汉式(静态常量)
- 饿汉式(静态变量)【可用】
====================================================================- 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了步问题
- 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading(懒加载)的效果。
- 如至终从未使用过这个实例,则会造成内存的浪费
class Singleton1{
/**
* 1.构造器私有化,防止外部new
*/
private Singleton1(){}
/**
* 在内部创建实例对象
* 在内加载的时候创建对象实例
*/
private final static Singleton1 instance=new Singleton1();
/**
* 对外提供一个共有的静态方法返回这个实例
*/
public static Singleton1 getInstance(){
return instance;
}
}
3.2饿汉式(静态代码块)
- 饿汉式(静态代码块)【可用】================================================================
- 优缺点:和饿汉式(静态变量)类似
- 只不过将类实例化的过程放在了静态代码块中,
- 也是在类装载的时候,就执行静态代码块中的代码,
- 初始化类的实例。优缺点和上面是一样的。
class Singleton2{
/**
* 1.构造器私有化,防止外部new
*/
private Singleton2(){}
/**
* 在静态代码块中创建实例对象
*/
private static Singleton2 instance;
static {
instance=new Singleton2();
}
/**
* 对外提供一个共有的静态方法返回这个实例
*/
public static Singleton2 getInstance(){
return instance;
}
}
四、懒汉式【不可用】
4.1懒汉式(线程不安全)【不可用】
- 懒汉式(线程不安全 )【不可用】========================================================================
- 优点:起到了Lazy Loading的效果,但是只能在单线程下使用。
- 缺点:如果在多线程下,一个线程进入了if (singleton == null)判断语块,
- 还未往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例在多线程环境下不可使用这种方式
class Singleton3{
private Singleton3(){}
private static Singleton3 instance;
/**
* 提供一个静态公有方法,当使用该方法时。才去创建instance
*/
public static Singleton3 getInstance(){
if(instance==null){
//没有创建才去创建
instance = new Singleton3();
}
//返回实例
return instance;
}
}
4.2懒汉式(线程安全,同步方法)【不可用】
- 懒汉式(线程安全 ,同步方法)【不可用】========================================================================
- 优点:解决了线程不安全问题
- 缺点:效率太低了,每个线程在想获得类的实例时候,
- 执行getlnstance()方法都要‘synchronized同步’。而其实这个方法只执行一次实例化代码就够了,
- 后面的想获得该直接return就行了。方法进行同步效率太低
- 结论:在实际开发中,效率低 不推荐使用这种方式
class Singleton4{
private Singleton4(){}
private static Singleton4 instance;
/**
* 提供一个静态公有方法,加入了同步处理的代码 ,解决线程安全问题
* 当使用该方法时。才去创建instance
*/
public static synchronized Singleton4 getInstance(){
if(instance==null){
//没有创建才去创建
instance = new Singleton4();
}
//返回实例
return instance;
}
}
4.3 懒汉式(线程安全 ,同步代码块)【不可用】
- 懒汉式(线程安全 ,同步代码块)【不可用】========================================================================
- 但是这种同步并不能起到线程同步的作用。跟第Singleton3实现方式遇到的情形一致,
- 假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,
- 另一个线程也通过了这个判断语句,这时便会产生多个实例。
class Singleton5{
private Singleton5(){}
private static Singleton5 instance;
/**
* 提供一个静态公有方法,加入了同步处理的代码 ,解决线程安全问题
* 当使用该方法时。才去创建instance
*/
public static Singleton5 getInstance(){
if(instance==null){
synchronized(Singleton5.class){
instance = new Singleton5();
}
}
//返回实例
return instance;
}
}
五、双重检查
- 双重检查=========================================================================
优点说明:
- Double-Check概念是多线程开发中常使用到的,如代码中所示,
- 我们进行次if (singleton == null)检查,这样就可以保证线程安全了
- 这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton =null)直接return实例化对象,
- 也避免的反复进行方法同步
- 线程安全;延迟加载;效率较高
- 结论: 在实际开发中,推荐使用这种单例设计模式
class Singleton6{
private static volatile Singleton6 instance;
private Singleton6(){}
/**
* 提供一个静态公有方法,加入双重检查的代码 ,解决线程安全问题,同时解决懒加载问题
* 也保证了效率
*/
public static synchronized Singleton6 getInstance(){
if(instance==null){
synchronized(Singleton6.class){
if(instance==null){
instance = new Singleton6();
}
}
}
//返回实例
return instance;
}
}
volatile
volatile
是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量,相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其使用也更容易出错。
下面简单总结一下volatile的作用:
-
它会强制将对缓存的修改操作立即写入主存,让所有的线程可见;
-
它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
-
如果是写操作,它会导致其他CPU中对应的缓存行无效。
六、静态内部类
- 这种方式跟饿汉式方式采用的机制类似,但又有不同。
- 两者都是采用了类装载的机制来保证初始化实例时只有一个线程。
- 不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,
- 没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,
- 而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
- 类的静态属性只会在第一次加载类的时候初始化,
- 所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
class Singleton7 {
private Singleton7() {
}
/**
* 静态内部类
*/
private static class SingletonInstance {
private static final Singleton7 INSTANCE = new Singleton7();
}
public static Singleton7 getInstance() {
return SingletonInstance.INSTANCE;
}
}
- 特点:
- 1.外部类装载时 静态内部类不会立即被装载
- 2.调用getInstance() 静态内部类才会被装载而且只会装载一次
- 优点:避免了线程不安全,延迟加载,效率高。
七、枚举
借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
public class Enumeration {
public static void main(String[] args) {
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println(singleton1==singleton2);
System.out.println(singleton1.hashCode());
System.out.println(singleton2.hashCode());
}
}
/**
* 枚举========================
*
* 借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
*/
enum Singleton{
//属性
INSTANCE;
public void sayOK(){}
}
八、适用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象。
功有所不全,力有所不任,才有所不足。欢迎大家指正文中错误
设计模式代码仓库 欢迎start====》设计模式代码仓库