文章目录
一、对象头的组成
二、为什么要使用对象头
三、对象头的使用具体说明
四、数据结构分析
五、代码演示
前言
每个对象都有对象头,对象头就可以把它理解成锁。jvm采用两个字节来存储对象头,如果对象是数组,则会分配3个字节,多出来的一个字记录的是数组长度。提示:以下是本篇文章正文内容,下面案例可供参考
一、对象头的组成?
1.mark word 存储对象的hashCode、锁等信息或分代年龄或GC标志等信息。 2.Class Metadata Address 类型指针指向对象的类元数据,JVM通过这个指针确定该对象是哪个类的实例。
二、为什么要使用对象头
由于Java面向对象的思想,在JVM中需要大量存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象功能,这些标记字段组成了对象头。
三、 对象头的使用具体说明
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210115095009896.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2RhZGF0aQ==,size_16,color_FFFFFF,t_70#pic_center) 进入waitSet队列的线程,必须要当前对象实例调用notify()唤醒,不然就一直处于阻塞状态。 # 四、数据结构分析 在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)ObjectMonitor() {
_header = NULL;
_count = 0; //记录个数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //处于等待锁的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
ObjectMonitor中有两个队列,_WaitSet和_EntryList,用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成ObjectWaiter对象),_owner 指向持有ObjectMonitor对象的线程地址。当多个线程同时访问一段同步代码时,首先会进入——EntryList集合,而当线程获取到锁的时候会进入到_Owner区域,并把monitor中的owner变量设置为当前线程同时monitor中的计数器count+1,而如果monitor的owner已经是当前线程,则再次尝试获取monitor,count+1(锁重入)。如果线程调用wait方法,将释放当前持有的monitor,owner变量恢复为null,count减1,同时该线程进入waitSet等待被notify唤醒。如果当前线程执行完毕也将释放monitor,以便其他线程获取。
五、代码演示
三个线程交替顺序打印ABC 题目描述:建立三个线程A、B、C,A线程打印10次字母A,B线程打印10次字母B,C线程打印10次字母C,但是要求三个线程同时运行,并且实现交替打印,即按照ABCABCABC的顺序打印。 方法: 使用synchronized, wait和notifyAll。 思路分析: 保证ThreadA->ThreadB->ThreadC按序执行,A想要执行,C必须得释放锁,B想要执行A就得释放锁,C想要执行B就得释放锁,三个线程成环。所以每个线程必须同时持有两个对象锁,才能进行打印。一个是prev,一个是自身对象锁。当前持有两个锁的线程调用notify唤醒下一个等待线程,只有等到同步代码块执行完毕后才会释放对象锁。再调用prev.wait立即释放prev对象锁。当前线程进入休眠,等待其他线程的notify操作再次唤醒。public class Synchronized_ABC {
public static void main(String[] args) throws Exception {
Object a=new Object();
Object b=new Object();
Object c=new Object();
ThreadPrinter pa=new ThreadPrinter("A",c,a);
ThreadPrinter pb=new ThreadPrinter("B",a,b);
ThreadPrinter pc=new ThreadPrinter("C",b,c);
new Thread(pa).start();
Thread.sleep(10);
new Thread(pb).start();
Thread.sleep(10);
new Thread(pc).start();
Thread.sleep(10);
}
public static class ThreadPrinter implements Runnable{
private String name;//线程名字
private Object prev;//前一个线程对应的对象锁
private Object self;//当前线程持有的对象锁
private ThreadPrinter(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 3;
while (count > 0) {// 多线程并发,不能用if,必须使用while循环
synchronized (prev) { // 先获取 prev 锁
synchronized (self) {// 再获取 self 锁
System.out.print(name);// 打印
count--;
self.notifyAll();// 唤醒其他线程竞争self锁,注意此时self锁并未立即释放。
}
//执行完self的同步块,进行锁释放
try {
if(count==0) {
prev.notify();//释放对象锁
}else {
prev.wait();//保证ABC顺序打印
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}