尚硅谷设计模式学习---[单例模式]
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尚硅谷设计模式学习---[单例模式]相关的知识,希望对你有一定的参考价值。
🚀🚀🚀尚硅谷传送门==>B站尚硅谷Java设计模式
❤❤❤感谢尚硅谷❤❤❤
🛴🛴🛴最近开始计划学习一下设计模式了,加油!!!
目录
单例模式
一个类把自己的构造方法隐藏起来;
这个类仅存在一个对象实例,且该类对外界只提供一个可以获取对象的方法.
单例模式使用的场景
:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(重量级对象),经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等).
单例模式主要有五种:
饿汉式
;懒汉式
; 双重检查
; 静态内部类
;枚举
🛴1.饿汉式(静态常量版)
先把类的构造方法用私有修饰符private
修饰,使其私有化;(外界不能通过new+构造方法的方式得到这个类的对象);
然后这个类在自己内部创建自己的对象;给外界提供一个公共方法去获取对象;
使用某类的static方法
;访问某类的static属性
;构造某类的对象
;通过Class.forName()来加载类
;都可以说是类加载了.
- 使用饿汉式(静态常量版)在类加载的时候就完成实例化。避免了线程同步问题。
- 需要注意的是类加载的方式有很多种,不能保证用的是
getInstance()
方法进行的类加载,那么此时的初始化singleton就没有懒加载的效果了;可能会浪费内存.
案例
public class StaticFinal {
public static void main(String[] args) {
//这里通过Singleton类提供的方法 ;获取Singleton类的对象;
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
//注意看看两次获取的是 同一个对象;
System.out.println(singleton1.hashCode());//460141958
System.out.println(singleton2.hashCode());//460141958
System.out.println(singleton1 == singleton2);// true
}
}
// 饿汉式 ( 静态变量 ) ;
class Singleton {
// 将构造方法私有化;
private Singleton() {
}
// 在自己内部创建对象;
private static Singleton singleton = new Singleton();
// 给外界提供一个获取对象的 静态公有方法;
public static Singleton getInstance() {
return singleton;
}
}
比如说Runtime
类;就是用了饿汉式的单例模式;
🛴2.饿汉式(静态代码块版)
类实例化的过程放在静态代码块中
public class StaticCodeBlock {
public static void main(String[] args) {
//这里通过Singleton类提供的方法 ;获取Singleton类的对象;
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1.hashCode());//460141958
System.out.println(singleton2.hashCode());//460141958
//注意看看两次获取的是 同一个对象;
System.out.println(singleton1 == singleton2);// true
}
}
// 饿汉式 ( 静态代码块 ) ;
class Singleton {
// 将构造方法私有化;
private Singleton() {
}
// 在自己内部定义引用;
private static Singleton singleton ;
// 在静态代码块中创建对象;
static {
singleton = new Singleton();
}
// 给外界提供一个获取对象的 静态公有方法;
public static Singleton getInstance() {
return singleton;
}
}
🛴3.懒汉式(线程不安全版)
懒汉式,在需要的时候才去创建实例,
达到了懒加载的效果,适合于单线程;
但是;由于这是单例模式;也就是说类仅有一份实例对象;要是放在多线程环境下;前面的线程创建了实例对象
singleton
之前; 正好有别的线程也执行了方法getInstance()
;它又去创建了一个实例对象.
实际开发不要用
public class ThreadNoSafe {
public static void main(String[] args) {
//这里通过Singleton类提供的方法 ;获取Singleton类的对象;
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1.hashCode());//460141958
System.out.println(singleton2.hashCode());//460141958
//注意看看两次获取的是 同一个对象;
System.out.println(singleton1 == singleton2);// true
}
}
// 懒汉式 线程不安全版;
class Singleton {
// 将构造方法私有化;
private Singleton() {
}
// 在自己内部定义引用;
private static Singleton singleton;
//给外界提供一个获取对象的方法;
public static Singleton getInstance() {
//当不存在实例对象时才创建;
if(singleton == null){
singleton=new Singleton();
}
return singleton;
}
}
🛴4.懒汉式(线程安全之同步方法版)
在上面的基础上;让这个提供给外界的方法getInstance()
变为同步方法;
也就是说,在多线程环境中,每个线程过来执行这个方法时,他要排队,也就没有那种(一个线程判断if(singleton == null)
后,还没创建好对象实例,别的线程也跑进来) 的情况;
但是这样子虽然说线程安全了,可是效率也变低了啊;本来这个getInstance()
方法就是为了得到实例对象的;把它变成同步方法;线程在这排着队;效率要低了.
public class ThreadSafeByMethod {
public static void main(String[] args) {
//这里通过Singleton类提供的方法 ;获取Singleton类的对象;
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1.hashCode());//460141958
System.out.println(singleton2.hashCode());//460141958
//注意看看两次获取的是 同一个对象;
System.out.println(singleton1 == singleton2);// true
}
}
// 懒汉式 线程安全 ==>同步方法 版;
class Singleton {
// 将构造方法私有化;
private Singleton() {
}
// 在自己内部定义引用;
private static Singleton singleton;
//给外界提供一个获取对象的方法; 此时getInstance() 为同步方法;
public static synchronized Singleton getInstance() {
//当不存在实例对象时才创建;
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
🛴5.懒汉式(线程安全之同步代码块版)
当然,同步方法版不好用,也有人用同步代码块的方式这样子写;
但是这种方式是存在着线程不安全的问题;就像第三种的一样;多线程环境下;一个线程判断if(singleton == null)
后,还没创建好对象实例,别的线程也跑进来;最后产生多个实例对象.
实际开发不要用
public class ThreadSafeByCodeBlock {
public static void main(String[] args) {
//这里通过Singleton类提供的方法 ;获取Singleton类的对象;
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1.hashCode());//460141958
System.out.println(singleton2.hashCode());//460141958
//注意看看两次获取的是 同一个对象;
System.out.println(singleton1 == singleton2);// true
}
}
// 懒汉式 线程安全 ==>同步代码块 版;
class Singleton {
// 将构造方法私有化;
private Singleton() {
}
// 在自己内部定义引用;
private static Singleton singleton;
//给外界提供一个获取对象的方法;
public static Singleton getInstance() {
//当不存在实例对象时才创建;
if (singleton == null) {
//用同步代码块;
synchronized (Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}
🛴6.双重检查版
注意: ==> 用
volatile
修饰的共享变量,每次的更新直接刷新到内存中去,对于其他线程都是可见的.
顾名思义,双重检查,判断检查两次;
这样的方式不但实现了懒加载效果,也是线程安全的,效率还高.
在多线程环境下,一个线程A
正在创建对象实例时,比如说线程B
也跑进来;这时候线程B
发现还要判断if(singleton == null)
;进行判断后,由于线程A
过去的时候已经创建了实例对象;那么线程B
自然就不用再去创建实例对象了.直接return返回吧.
还是比较推荐使用的方式
public class DuplicationCheck {
public static void main(String[] args) {
//这里通过Singleton类提供的方法 ;获取Singleton类的对象;
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1.hashCode());//460141958
System.out.println(singleton2.hashCode());//460141958
//注意看看两次获取的是 同一个对象;
System.out.println(singleton1 == singleton2);// true
}
}
// 双重检查版;
class Singleton {
// 将构造方法私有化;
private Singleton() {
}
// 在自己内部定义引用;
// 用 volatile修饰的共享变量,每次的更新对于其他线程都是可见的 ;
private static volatile Singleton singleton;
//给外界提供一个获取对象的方法;
public static Singleton getInstance() {
//当不存在实例对象时才创建;
if (singleton == null) {
//用同步代码块;
synchronized (Singleton.class){
//在这里再次进行判断检查;
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
🛴7.静态内部类版
首先回顾一下静态内部类;
当外部类加载时,静态内部类并不会被实例化;
当需要去调用
getInstance()
方法时,静态内部类才去加载;且仅加载一次.
(类的静态属性只会在第一次加载类的时候初始化)
(类初始化时,别的线程无法进入)
线程安全,有延迟加载效果,效率高
public class StaticInnerClass {
public static void main(String[] args) {
//这里通过Singleton类提供的方法 ;获取Singleton类的对象;
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1.hashCode());//460141958
System.out.println(singleton2.hashCode());//460141958
//注意看看两次获取的是 同一个对象;
System.out.println(singleton1 == singleton2);// true
}
}
//静态内部类的方式
class Singleton {
// 将构造方法私有化;
private Singleton() {
}
//用静态内部类定义创建的私有静态常量对象;
private static class SingletonObj {
//在自己内部创建对象;
private static final Singleton SINGLETON = new Singleton();
}
// 给外界提供一个获取对象的方法;
public static Singleton getInstance() {
//调用此方法时,才去加载静态内部类;
return SingletonObj.SINGLETON;
}
}
🛴8.枚举版
可解决线程同步问题,也防止反序列化对象
推荐使用
public class ByEnum {
public static void main(String[] args) {
//这里通过Singleton类提供的方法 ;获取Singleton类的对象;
Singleton singleton1=Singleton.INSTANCE;
Singleton singleton2=Singleton.INSTANCE;
System.out.println(singleton1.hashCode());//460141958
System.out.println(singleton2.hashCode());//460141958
//注意看看两次获取的是 同一个对象;
System.out.println(singleton1 == singleton2);// true
}
}
//使用枚举的方式;
enum Singleton{
INSTANCE;
}
以上是关于尚硅谷设计模式学习---[单例模式]的主要内容,如果未能解决你的问题,请参考以下文章
尚硅谷设计模式学习(23)---[策略模式(strategy pattern)]
尚硅谷设计模式学习---[简单工厂模式,工厂方法模式,抽象工厂模式]