Java高并发程序设计学习笔记(二)锁相关

本文详细介绍了Java中锁的种类,包括偏向锁、自旋锁、轻量级锁和重量级锁,以及它们的工作原理和使用场景。同时,探讨了synchronized关键字、volatile关键字和AtomicXxx类的线程安全特性,分析了锁的底层实现和对象内存布局。

锁的种类

Java中的锁大致分为:偏向锁,自旋锁,轻量级锁,重量级锁

锁的使用方式为:先提供偏向锁,如果不满足时,升级为轻量级锁,再不满足,升级为重量级锁。自旋锁是一个过渡的锁状态,不是一种实际的锁类型。锁只能升级,不能降级。

 

偏向锁

是一种编译解释锁。如果代码中不可能出现多线程并发争抢同一个锁的时候,JVM编译代码,解释执行的时候,会自动的放弃同步信息。消除synchronized的同步代码结果。使用锁标记的形式记录锁状态。在Monitor中有变量ACC_SYNCHRONIZED。当变量值使用的时候,代表偏向锁锁定。可以避免锁的争抢和锁池状态的维护,提高效率。

 

轻量级锁

过渡锁。当偏向锁不满足时,也就是有多线程并发访问,锁定同一个对象的时候,先提升为轻量级锁。也是使用ACC_SYNCHRONIZED标记记录的。ACC_UNSTNCHRONIZED标记用于记录未获取到锁信息的线程。就是只有两个线程争抢锁标记的时候,优先使用轻量级锁。

两个线程也可能出现重量级锁。

 

自旋锁

是一个过渡锁,时偏向锁和轻量级锁的过渡。

当获取锁的过程中,未获取到。为了提高效率,JVM自动执行若干次空循环,再次申请锁,而不是进入阻塞状态的情况。称为自旋锁。自旋锁提高效率就是避免线程状态的变更。

 

 

 

锁(重量级锁)

synchronized关键字

锁资源 synchronized(Object) ,锁对象资源。

锁对象 synchronized(this) 和synchronized方法都是锁当前对象

同步方法--static

静态同步方法,锁的是当前类型的类对象。

 

锁可重入,同一个线程多次调用同步代码,锁定同一个锁对象,可重入。

 

 

volatile关键字

volatile的可见性,通知OS操作系统底层,在CPU计算过程中,都要检查内存中数据的有效性,保证最新的数据被使用。volatile只能保证可见性,不能保证原子性。不是加锁的问题,只是内存数据可见。

 

AtomicXxx

同步类型

原子操作类型,其中的每一个方法都是原子操作,可以保证线程安全。

 

 

锁对象变更问题: 同步代码一旦加锁后,那么会有一个临时的锁引用执行所对象,和真实的引用无直接关联。在锁未释放之前,修改锁对象引用,不会因影响同步代码的执行。

 

 

CountDownLatch:门闩

可以和锁混合使用,或者替代锁的功能。

在门栓未完全开访之前等待,当门栓完全开放后执行。

避免锁的效率低下问题.

 

1. 锁的底层实现

Java虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。同步方法,并不是由monitor.enter和monitor.exit指令来实现,而是通过方法调用指令读取运行时常量池中方法的ACC_SYNCHRONIZED标志来隐式实现的。

 

对象内存简图

 

 

 

对象头:存储对象的hashcode、锁信息,分代年龄或者GC标志,类型指针指向对象的类元数据,JVM通过这个指针确定该对象是哪个类的实例等信息。

实例变量:存储类的属性数据信息,包括父类的属性信息。

填充数据: 由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须的,仅仅是为了字节对齐。

 

当给对象加锁时,数据时记录在对象头中。当执行synchronized同步方法或者同步代码块时,会在对象头中记录锁标记,锁标记指向的时monitor对象(也称为管程或者监视器锁)的起始地址。每个对象都存在着一个monitor与之关联,对象与其monitor之间的关系又存在多种实现方式,如monitor可以与对象一起创建销毁,或者当线程试图获取对象锁时自动生成。但当一个monitor被某个线程持有后,它便处于锁定状态。

 

在Java虚拟机(HotSpot)中,monitor是由ObjecMonitor实现的。

ObjectMonitor中有两个队列,_waitSet和_EntryList,以及_Owner标记。其中_WaitSet是用于管理等待队列线程的,_EntryList是用于管理锁池组塞线程的,_Owner标记用于记录当前执行线程。

 

线程状态图:

 

 

当多线程并发访问同一个同步代码时,首先会进入_EntryLsit,当线程获取锁标记后,monitor中的_Owner记录此线程,并在monitor中的计数器执行递增计算(+1),代表锁定,其他线程在_EntryList中继续阻塞。若执行线程调用wait方法,则monitor中的计数器执行赋值为0计算,并将_Owner标记赋值为null,代表放弃锁,执行线程进入waitSet中阻塞。若执行线程调用notify/notifyAll方法,_WaitSet中的线程被唤醒,先就绪然后进入_EntryLIst中阻塞,等待获取锁标记。若执行线程的同步代码执行结束,同样会释放锁标记,monotor中的_Owner标记赋值为null,且计数器赋值为0。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值