java补充知识点

CAS算法

  1. 概念:
    线程安全类(如ConcurrentMap、ConcurrentLinkedQueue等线程安全集合。)底层实现使用的一种称为CAS的算法(Compare And Swap)。

  2. CAS中的三个值:

  • 内存值 N
  • 预期值 E
  • 要更新的值 V

假设有两个线程 t1,t2;
主内存空间的值为6,t1,t2 会把主内存的值完全拷贝一份到自己的工作内存空间,所以 t1,t2 的 预期值都为 6
假设 t1 在与 t2线程竞争中,t1拿到了CPU执行权,则其他线程全部进入就绪态,然后 t1 更新内存中的值为 7,此值写在了内存中,而往后若 t2或其他线程来更新内存,会发现内存中的值为 7 ,与预期值不同,就会操作失败。

  1. 该算法相对 synchronized 是乐观的
    乐观锁:总是认为线程是安全的,不怕别的线程修改变量,修改了就再重新尝试
    悲观锁:总是认为线程不安全,不管什么情况先加锁,要是获取锁失败,就阻塞。
  2. 优点:
    这个算法相对 synchronized 是乐观的,它不会像前者一样,当一个线程访问共享数据的时候,其他线程都在阻塞,synchronized 不管是否有线程冲突,都会进行加锁,所以有如下优点:
  • 线程的相互影响特别小。
  • 使用无锁的方式完全没有锁竞争带来的系统开销。
  • 也没有线程频繁调度带来的开销
    在这里插入图片描述
    缺点:
  • 循环时间太长
  • 只能保证一个共享变量原子操作
  • 会出现ABA问题

综上所述:
CAS其实就是拿副本中的预期值与主内存中的值进行比较,如果相等就继续替换新的值,不相等就说明主内存中的值已经被修改,继续重试,CAS是CPU指令级的操作,非常快。

Gson

  1. 简介:
    Gson是Google提供的用来在Java对象和JSON数据之间进行映射的Java类库,可以将一个Json字符转成一个Java对象,反之亦可(序列化/反序列化)!
  2. 特点:
  • 快速,高效。
  • 代码量少,简介。
  • 面向对象。
  • 数据传递和解析方便

类加载器

1. 类的加载:

  • 由下图可知,我们写好的Java代码会通过编译成为字节码文件(.class文件 / 二进制数据),这个文件并不能直接被JVM使用,而是通过类加载器将其加载到JVM内存中去;
  • 且它不是原封不动的加载进入,而是将 .class文件中的内容转换成Java虚拟机使用的类字节码,比如说Java程序中的字符串编译(.class)文件后是以UTF-8编码存放的,但是装载到Java虚拟机后就成了Unicode编码了。
  • 通过类加载器装载到Java虚拟机中的字节码数据,成了可执行的代码
    在这里插入图片描述
  • 何时启动类加载器:
    类加载器会在某个类将要被使用的时候预先加载它,若在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器会在程序首次主动使用此类时报告错误(LinkageError)错误,若这个类一直没有被程序主动调用,那么类加载器就不会报错!
  • 从哪个地方去加载 .class文件
    (1)本地磁盘
    (2)网上加载 .class 文件
    (3)从数据库中
    (4)压缩文件中(ZAR,jar等)
    (5)从其他文件生成(JSP应用)

2. 类加载(类加载器把类加载入Java虚拟机)的过程:

概述:

  • 类从被加载到虚拟机内存开始,到卸载出内存为止,他的生命周期包括:加载,验证,准备,解析,初始化,使用,卸载七个阶段,如图:
    在这里插入图片描述
  • 其中类的加载包括了:加载,验证,准备,解析,初始化五个阶段。
  • 而这五个阶段中只有加载,验证,准备,初始化这四个阶段发生的顺序是确定的,解析在某些情况下可能会在初始化之后开始。
  • 确定顺序的四个阶段是按顺序开始的,而不是按顺序进行或完成,这些阶段通常都是互相交叉的混合进行的,通常会在阶段执行的过程中调用或激活另一个阶段。

  1. 加载
    在加载阶段,虚拟机主要完成三件事:
    (1)通过一个类的全限定名(包名 + 类型)来获取其定义的二进制字节流(即获取该类的二进制形式/将类的.class文件读入到内存中);
    (2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
    (3)在堆中生成一个代表这个类的Class(java.lang.Class)对象,作为方法区中这些数据的访问入口。

相对于类加载的其他阶段而言,加载阶段是可控性最强的阶段,因为程序员可以使用系统的类加载器进行加载,也可以使用自己的类加载器加载。

  1. 验证(连接)
    验证的主要作用就是确保被加载的类的正确性,检验其是否有正确的内部结构,确保加载好的 .class 文件不能对虚拟机有危害,验证主要分为四个阶段:

    a.文件格式验证:
    验证 .class文件字节流是否符合class文件的格式规范,并且能够 被当前版本的虚拟机处理。

    b.元数据验证:
    主要是对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求,比如验证这个类是否有父类,类中的字段方法是否和父类冲突等。

    c.字节码验证:
    这是整个验证中最复杂的阶段,主要是通过数据流和控制流分析,确定程序语义是合法的,符合逻辑的;在元数据阶段对数据类型做出验证后,这个阶段主要对类的方法做出分析,保证类的方法在运行时不会危害虚拟机。

    d.符号引用验证:
    这一步发生在虚拟机将符号引用转化为直接引用的时候,主要是对类自身以外的信息进行校验,确保解析动作能够完成,确保类的引用一定会被访问到,不会出现类无法访问问题。

  2. 准备(连接):
    准备阶段主要为类变量分配内存并设置初始值。这些内存都在方法区分配,这里需要注意两个关键词:类变量,初始值
    (1)类变量(static)会分配内存,但实例变量不会,实例变量主要随着对象的实例化一块分配到Java堆中。

    (2)这里的初始值指的是数据类型的默认值,而不是在代码中我们赋的值,例如 public static int value = 1; 这里准备阶段过后value的值为0而并不是1,赋值为一的动作在初始化阶段。
    其他数据类型的默认值:
    在这里插入图片描述

注意:在上面 value 是被 static 所修饰的准备阶段之后是0,但是如果同时被 final 和 static 修饰,准备阶段过后就是
1 了,可以理解为 static final 在编译器就将结果放入调用它的常量池中了。

  1. 解析(连接):
    该阶段主要是将虚拟机常量池中的符号引用转化为直接引用的过程。

符号引用:
以一组符号来描述所引用的目标,可以是任意形式的字面变量,只要是能无歧义的定位到目标就好,即一个代号(符号),指向一个目标;

直接引用:
直接引用是可以指向目标的指针,相对偏移量,或一个能够直接或间接定位目标的句柄。和虚拟机实现的内存有关,不同的虚拟机直接引用一般不同。

解析动作主要针对类,接口,类方法,接口方法,方法句柄

  1. 初始化:
  • 在这个阶段,Java代码才是真正开始执行,且在准备阶段已经为变量赋过一次值,在初始化阶段我们可以根据自己的需求来赋值。
  • 在该阶段,主要为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要是对类变量进行初始化,有两种方式:

(1)声明类变量是指定初始值
(2)使用静态代码块为类变量指定初始值

JVM初始化步骤:

  1. 假如这个类还没有被加载和连接,则程序先加载并连接该类

  2. 假如该类的直接父类没有初始化,则先初始化父类

  3. 假如类中有初始化语句,则系统依次执行这些初始化语句

类初始化时机:
只有主动使用类的时候才会导致类的初始化,类的主动使用包括以下几种:

  1. 创建类的实例,也就是new对象
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射
  5. 初始化某个类的子类,其父类也会被初始化
  6. java虚拟机启动时被标明为启动类的类(javaTest),直接使用Java。exe命令来运行某个主类。

类加载的三种方式:

(1)通过命令行启动应用时由JVM初始化加载含有main方法的主类。
(2)通过Class.forName()方法动态加载,会默认执行初始化块(static{}),但是Class.forName(name,initialize,loader)中的 initialize可指定是否要执行初始化块。
(3)通过ClassLoader.loadClass()方法动态加载,不会执行初始化块。

3. 类加载器:

  • 类加载器负责加载所有类,为所有被载入内存的类生成一个java.lang.Class实例对象,同一个类被载入至内存后便不会再被载入了,载入JVM的类有一个唯一的标识。
  • 类加载器本身也是一个Java虚拟机中内嵌的一个类,类库中提供了一个Java.lang.ClassLoader作为类的装载基类,也就是说所有的类加载器必须都是ClassLoader的子类,且所有的自定义类加载器也需要被类加载器加载。
  1. Java语言系统自带的三个类加载器:
  • Bootstrap ClassLoader(启动类(根类)加载器)
    他是用来加载java的核心类,使用原生代码来实现的,并不继承自java.lang.ClassLoader;由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接引用进行操作。

  • Extention ClassLoader(扩展类加载器)

  • Appclass Loader(应用类加载器)

  1. 自定义类加载器:
  • 两种方式:
    (1)遵守双亲委派模型:继承ClassLoader,重写findClass()方法。

    (2)破坏双亲委派模型:继承ClassLoader,重写loadClass()方法。

  • 实现步骤:

    (1)创建一个类继承ClassLoader抽象类
    (2)重写findClass()方法
    (3)在findClass()方法中调用defineClass()

  1. 类加载器加载Class的步骤:

(1)检测此Class是否被载入过,即在缓冲区是否有此Class,如果有直接进入第八步

(2)如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,进入第四步;如果有父类加载器,则进入第三步

(3)请求使用父类加载器去载入目标,若载入成功进入第八步,否则进入第五步

(4)请求使用根类加载器载入目标,载入成功进入第八步,否则跳至第七步

(5).当前类加载器尝试寻找Class文件,若找到进入六,否则七

(6) 从文件载入Class,成功后进入八

(7)抛出ClassNotFountException异常

(8)返回对应的java.lang.Calss对象

  1. 类加载器树状组织结构示意图:
    在这里插入图片描述

4. 类加载机制:

  1. 全盘负责:
    当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另一个类加载器来载入

  2. 双亲委派:

  • 工作流程:
    a. 类加载器收到类加载任务

    b. 优先交给父类加载器完成(因此最终加载任务都会传递到顶层的启动类加载器)

    c. 当父类加载器无法完成加载任务,此类加载器才尝试执行加载任务

  • 优点:
    无论哪个加载器加载一个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同一个Object对象。
    a. 可以避免重复加载,父类已经加载了,子类就不需要再次加载
    b. 更加安全,很好的解决了各个类加载器的基础类的统一问题,如果不使用该种方式,那么用户可以随意定义类加载器来加载核心api,会带来相关隐患。

  1. 缓存机制:
    缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要某个类时,类加载器先从缓存区搜寻该Class,只有缓存区不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换为Class对象,存入缓冲区中,这就是为什么修改了Class文件后,必须重新启动JVM,程序所作的修改才会生效。

5. java.lang.ClassLoader类介绍:

  • 基本上所有类加载器都是这个类的一个实例
  • 该类的基本职责是根据一个指定的类的名称,找到或生成其对应的字节代码,然后从这些字节代码中定义一个java类,即java.lang.Class类的一个实例,除此之外,ClassLoader还负责加载Java应用所需的资源,如图像文件和配置文件等
  • 为了完成加载类的职责,ClassLoader提供了一系列方法:
    (其中,方法形参name是二进制名称)
    在这里插入图片描述
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值