当前位置: 首页 > news >正文

受欢迎的建网站哪家好安庆网站建设工作室

受欢迎的建网站哪家好,安庆网站建设工作室,泰安招聘,广州网站排名怎么优化目录 背景前置知识类加载运行全过程 单例模式的实现方式一、饿汉式基本介绍源码分析 二、懒汉式基本介绍源码分析改进 三、懒汉式单例终极解决方案(静态内部类)(推荐使用方案)基本介绍源码分析 感谢 背景 最近学习了JVM之后&…

目录

  • 背景
  • 前置知识
    • 类加载运行全过程
  • 单例模式的实现方式
    • 一、饿汉式
      • 基本介绍
      • 源码
      • 分析
    • 二、懒汉式
      • 基本介绍
      • 源码
      • 分析
      • 改进
    • 三、懒汉式单例终极解决方案(静态内部类)(推荐使用方案)
      • 基本介绍
      • 源码
      • 分析
  • 感谢

背景

最近学习了JVM之后,总感觉知识掌握不够深,所以想通过分析经典的【懒汉式单例】来加深一下理解。(主要是【静态内部类】实现单例的方式)。
如果小白想理解单例的话,也能看我这篇文章。我也通过了【前置知识】跟【普通懒汉式】、【双检锁懒汉】、【静态内部类】懒汉给大家分析了一下他们的线程安全性。但是,我这边没有完整的演进【懒汉式单例】历程。所以,会缺少思维上的递进。不过,我在最后的【感谢】名单里,提供了一个完整的【懒汉式单例演进】的链接,建议可以结合这个文章一起学习。

前置知识

类加载运行全过程

当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把主类加载到JVM。

package com.tuling.jvm;public class Math {public static final int initData = 666;public static User user = new User();public int compute() {  //一个方法对应一块栈帧内存区域int a = 1;int b = 2;int c = (a + b) * 10;return c;}public static void main(String[] args) {Math math = new Math();math.compute();}
}

通过Java命令执行代码的大体流程如下:
在这里插入图片描述
其中loadClass的类加载过程有如下几步:
加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载

  • 加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
  • 验证:校验字节码文件的正确性
  • 准备:给类的静态变量分配内存,并赋予默认值
  • 解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用,下节课会讲到动态链接
  • 初始化:对类的静态变量初始化为指定的值,执行静态代码块
    在这里插入图片描述

总结一下,上面说的加载 >> 验证 >> 准备 >> 解析 >> 初始化过程是由JVM帮我们进行的,所以,对我们程序员来说,【天生】就具备线程安全性(这个由JVM帮我们保证,无需我们关心)。

单例模式的实现方式

单例模式,是我们Java中很常见的一个设计模式。所以有这么一种说法:遇事不决,单例解决。
Java单例通常有2种,分别为:饿汉式、懒汉式

一、饿汉式

基本介绍

饿汉式(Eager Initialization,急切的初始化),在类加载时就创建单例实例,并在需要时直接返回该实例。这种方式的实现是线程安全的,因为在类加载过程中实例已经创建好了。

源码

public class SingletonTest {private static final SingletonTest me = new SingletonTest();public static SingletonTest me() {return me;}public static void main(String[] args) {System.out.println(SingletonTest.me());System.out.println(SingletonTest.me());System.out.println(SingletonTest.me());}
//    系统输出如下:
//    org.tuling.juc.singleton.SingletonTest@12a3a380
//    org.tuling.juc.singleton.SingletonTest@12a3a380
//    org.tuling.juc.singleton.SingletonTest@12a3a380
}

分析

因为单例对象SingletonTest 是静态成员变量,所以,在JVM类加载过程中==(加载-》验证-》准备-》解析-》初始化)==的【解析】阶段已经被JVM初始化了,所以,由JVM保证了线程安全性。

二、懒汉式

基本介绍

懒汉式(Lazy Initialization),在首次调用时创建单例实例,存在线程安全问题。如果多个线程同时进入判断条件,可能会创建多个实例。

源码

public class SingletonTest {private static SingletonTest me;public static SingletonTest me() {if(me == null) {me = new SingletonTest();}return me;}public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{System.out.println(SingletonTest.me());}).start();}}
}

输出结果如下
在这里插入图片描述

分析

为什么上面这段代码不是线程安全的呢?我们举一个极端的例子,如下图所示:
在这里插入图片描述
在没有锁机制的存在情况下,多线程环境里面可能会出现上述的并发执行情况。在线程1判断完me == null之后,即将开始执行new之前,线程2也刚好在判断me == null,这是因为线程1还没有执行new操作,所以线程2判断肯定是null的,于是也开始new。这就是线程安全问题所在。
(PS:小白们一定要理解上面这个图。虽然很简单,但是说它是你们迈向,或者培养【并发意识】的启蒙都不为过。)

改进

为了解决上面的问题,大牛们进行了改进,使用了【双检锁+volatile】机制,【双检锁】,即:双重检查锁。代码如下:

public class SingletonTest {private static volatile SingletonTest me;public static SingletonTest me() {if(me == null) {synchronized (SingletonTest.class) {if (me == null) {me = new SingletonTest();}}}return me;}
}

上面的改进,关键点如下:

  1. 使用了volatile关键字修饰单例对象me
  2. 在获取单例对象的时候,判断了两次if(me == null)
  3. 第二次判断if(me == null)之前,先加了锁

第二、三点我就不说了,大家可以看看最下面【感谢】的友链。这里重点说说第一点。
估计小白会很难理解,为什么一定要volatile关键字修饰,不用可以吗?答案是:不可以。因为,volatile能禁止重排序。什么是【重排序呢】?说的简单点,就是JVM,甚至是CPU为了性能,可能会在不改变语义的情况下修改我们的代码执行顺序。比如,当我们new SingletonTest()的时候,你以为只有一步操作,实际上,它有3步,如下:

memory = allocate(); // 1.分配对象内存空间
instance(memory); // 2.初始化对象
instance = memory; // 3.设置instance指向刚分配的内存地址,此时instance!=null

但事实上,经过重排序之后可能会变成下面的执行顺序:

memory = allocate(); // 1.分配对象内存空间
instance = memory; // 3.设置instance指向刚分配的内存地址,此时instance!=null
instance(memory); // 2.初始化对象

然后大家再用上面的【并发启蒙】意识,自己画个图看下,还能线程安全吗?
所以,需要使用volatile关键字,告诉底层JVM或者CPU,不要帮我重排序这个对象!于是就避免了上面的并发线程安全问题了。

三、懒汉式单例终极解决方案(静态内部类)(推荐使用方案)

基本介绍

这里通过利用JVM类加载【天生线程安全】的特性,来帮助实现【懒汉式】的单例。如何做到呢?答案是【静态内部类】。

源码

public class SingletonTest {/** 单例对象,可以直接调用配置属性  */private static class Holder {private static SingletonTest me = new SingletonTest();}public static SingletonTest me() {return Holder.me;}public static void main(String[] args) {int threadCount = 10000;for (int i = 0; i < threadCount; i++) {new Thread(()->{System.out.println(SingletonTest.me());}).start();}}
}

上面的代码,新建了1W个线程来调用单例,我们发现,结果都是一样,同一个对象。
在这里插入图片描述

分析

为什么上面,通过静态内部类能保证线程安全性呢?这个我们在【前置知识】已经说过了,是由JVM保证了线程安全性。
在这里插入图片描述
如上图所示,只有当我们使用了SingletonTest.me()的时候,才会去开始加载Holder静态内部类,这就是它实现【懒汉式】的原因(延迟加载)。

感谢

感谢【作者:weixin_47196090】的深度好文,《懒汉式单例演进到DCL懒汉式 深度全面解析》

http://www.yayakq.cn/news/40435/

相关文章:

  • 做网站行情基于jsp的网站开发开题报告
  • 淘宝网站开发成本90设计网站免费素材
  • 房产网站建设方案的论文婚礼摄影作品网站
  • 公司网站建设算什么费用wordpress的ico
  • 宜州做网站需要多少钱百度百科优化排名
  • 自建的电子网站如何做推广巨鹿网站建设网络公司
  • 食品公司网站模板seo大连培训
  • 网站开发团队需配备什么岗位展厅设计理念和设计构思
  • 做淘宝客新增网站推广沈阳建设网站服务公司
  • 海南专业网站运营托管wordpress微信显示图片
  • 不是网站开发语言的是看怀集app下载
  • php门户网站源码源码交易网站源码
  • 网站后台建设软件火车头wordpress免登录发布
  • 重庆市建设监理协会网站山西新闻头条最新消息
  • 如何做网站公众号推广百度网站的建设目标
  • 时间轴网页网站模板免费建立平台网站
  • 注册top域名做公司网站php网络公司企业网站源码(万网idc代理网站源码)
  • 网站后台任务python爬虫爬小说来做网站
  • 南阳网站seo公司html技术的简介
  • 做爰的细节描述和过程网站删除后台wordpress新闻
  • 用php做网站的原理湖州网站设计浙北数据
  • 成都网站建设技术外包阿里云 wordpress主机
  • 手机网站技巧设计网站设计原则
  • 上海网站建设外包公司负责公司网站的更新和维护
  • 青山网站建设创业过程中网站建设
  • 网站风格抄袭网站admin后台界面模板
  • 株洲网站优化广州番禺天气
  • 重庆网站建设招标玉环住房与城乡建设规划局网站
  • 网站更新步骤大良做网站
  • 网站建设的基本术语wordpress宽度