对象头Monitor

文章目录

一、对象头的组成
二、为什么要使用对象头
三、对象头的使用具体说明
四、数据结构分析
五、代码演示


前言

每个对象都有对象头,对象头就可以把它理解成锁。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();
                    }
                }
            }
        }
    }


}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值