最新基础知识梳理

1、java对象创建时机/方式

(1)使用new关键字来调用一个类的构造函数显式地创建对象

(2)反射机制(Class类的newInstance方法(调用无参构造器)、使用Constructor类的newInstance方法(更强大,可以调用有参数和私有的构造函数))

(3)使用Clone方法(实现Cloneable接口并实现其定义的clone方法)

(4)使用反序列化等方式创建对象(类实现Serializable接口,当我们反序列化一个对象时,JVM会给我们创建一个单独的对象)

2、对象的组成

(1)对象头:markword【哈希码、GC分代年龄、锁标识状态、线程持有的锁、偏向线程ID】

                       指向类的指针、

                       (数组长度)

(2)实例数据:对象的实例数据就是在Java代码中能看到的属性和他们的属性值

(3)对其填充:JVM要求Java的对象占的内存大小应该是8bit的倍数

3、对象创建过程(以new为例)

(1)检查类是否已经被加载。类的加载过程:加载、链接(为静态变量赋默认值)、初始化(执行类的初始化方法clinit,为静态变量赋初始值,clinit方法包含静态变量、静态代码块、按照生命的顺序执行)

(2)为对象分配内存空间(堆中,私有)

          有两种分配方式:指针碰撞,虚拟机的内存是地址连续的,带有内存压缩机制来形成连续地址空间。但这种方式在多线程时会出现指针划分不一致的问题。方式二是虚拟机为每个线程分配不同的空间,这个空间称为“本地线程分配缓冲”(TLAB)

(3) 为对象的字段赋默认值(0、null)

(4)设置对象头:对这个将要创建出来的对象,进行信息标记,包括是否为新生代/老年代,对象的哈希码,元数据信息,这些标记存放在对象头信息中。

(5)执行实例的初始化方法init: init方法包含成员变量构造代码块的初始化,按照声明的顺序执行。

(6)执行构造方法:对象创建成功

上述为无父类的对象创建过程。对于有父类的对象创建过程,还需满足如下条件:

  1. 先加载父类;再加载本类;
  2. 先执行父类的实例的初始化方法init(成员变量、构造代码块),父类的构造方法;执行本类的实例的初始化方法init(成员变量、构造代码块),本类的构造方法。

总结对象创建过程

1、先加载类

父类链接:为静态变量赋默认值

父类初始化:为静态变量赋初始值

子类链接:为静态变量赋默认值

子类初始化:为静态变量赋初始值

2、为成员变量赋默认值

父类:为成员变量赋默认值

子类:为成员变量赋默认值

3、父类:对象初始化,为成员变量赋初始值

4、父类:执行构造方法

5、子类:对象初始化,为成员变量赋初始值

6、子类:执行构造方法

4、锁升级过程(记录在markword中)

1)当对象没有锁时,这就是一个普通的对象,MarkWord记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。锁状态为无锁。
2)当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。
3)当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,MarkWord中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。
4)当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是MarkWord中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把MarkWord里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。
5)偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁MarkWord的指针,同时在对象锁MarkWord中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把MarkWord中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。
6)轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。
7)自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。

5、redis实现分布式锁

(1)set key value nx ex seconds   将锁和过期时间编排到一起,它们是原子操作,可以避免死锁

(2)解锁可能出现解的不是自己的,所以在加锁时给锁设置一个标识,当进程解锁时需要进行比对,然后才能释放

(3)为了保证解锁两个步骤的原子性,使用lua脚本

(4)单个主节点没问题了,但如果存在主从情况,进程A在主节点上加锁,但是主宕掉了;将从节点晋升为主,此时进程B给新的主节点加锁。之后原主节点重启成为从节点,这时系统会出现两把锁,违背唯一性原则。建议使用redlock算法。
 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值