深入了解Java线程锁(一)

在上一篇《如何保证线程的原子性》中,我们谈到了锁(Synchronized),
这次我们就来深入探讨一下Java多线程中的锁。


互斥锁的本质是共享资源。
在这里插入图片描述

如上图所示,

  1. Thread1访问受保护资源,对其加锁,将受保护资源的锁加成粉红色。
  2. 这时Thread2来访问,发现锁对象是红色的,就阻塞等待。
  3. 当Thread1 释放锁后,锁恢复为绿色,表示资源可以使用。
  4. Thread2 可以正常访问资源。
    在这里插入图片描述
    上面例子中的粉色、绿色,其实就是锁对象的一些属性,这些属性存储在哪里呢?

接下来我们就来说一下 锁的存储
锁的存储,跟对象头有关系。
在hotspot 虚拟机中,对象头就是表示一个对象在内存中的布局。
在这里插入图片描述
比如我们new了一个lock对象,那么它在虚拟机中会构建一个对象布局。
我们从虚拟机源码中可以看出一些端倪。
instanceOop.hpp 文件中:

#ifndef SHARE_VM_OOPS_INSTANCEOOP_HPP
#define SHARE_VM_OOPS_INSTANCEOOP_HPP

#include "oops/oop.hpp"

// An instanceOop is an instance of a Java Class
// Evaluating "new HashTable()" will create an instanceOop.

class instanceOopDesc : public oopDesc {
 public:
  // aligned header size.
  static int header_size() { return sizeof(instanceOopDesc)/HeapWordSize; }

  // If compressed, the offset of the fields of the instance may not be aligned.
  static int base_offset_in_bytes() {
    // offset computation code breaks if UseCompressedClassPointers
    // only is true
    return (UseCompressedOops && UseCompressedClassPointers) ?
             klass_gap_offset_in_bytes() :
             sizeof(instanceOopDesc);
  }

  static bool contains_field_offset(int offset, int nonstatic_field_size) {
    int base_in_bytes = base_offset_in_bytes();
    return (offset >= base_in_bytes &&
            (offset-base_in_bytes) < nonstatic_field_size * heapOopSize);
  }
};

#endif // SHARE_VM_OOPS_INSTANCEOOP_HPP

每个Java的Class实例,都会对应一个这样的 instanceOop
来看一下它的父类 oopDesc

class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark;
  union _metadata {
    Klass*      _klass;
    narrowKlass _compressed_klass;
  } _metadata;
  ......
}

oopDesc 中的 markOop 就是我们上图中的对象头, _metadata 是元数据。
我们打开 markOop.hpp文件,可以看到对象头的每位所存储的内容的注释:

在这里插入图片描述
通过上述内容,可以知道,锁对象中有个空间来存储锁的状态。
当线程来临的时候,就可以通过锁状态来判断,是否可以进入到当前代码。

我们可以使用jol-core 这个工具来打印出来类的布局,加深理解。
代码pom.xml中加入:

   <dependency>
       <groupId>org.openjdk.jol</groupId>
       <artifactId>jol-core</artifactId>
       <version>0.10</version>
   </dependency>

编写一个类,输出一个无锁状态下的打印:

import org.openjdk.jol.info.ClassLayout;

public class MyClassLayout {
    public static void main(String[] args) {
        MyClassLayout myClassLayout = new MyClassLayout();
        System.out.println(ClassLayout.parseInstance(myClassLayout).toPrintable());
    }
}

执行结果:

demo.MyClassLayout object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


Process finished with exit code 0

对象头的每位所存储的内容可以通过下表来看:
在这里插入图片描述


再对照上述打印出的类布局:

      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)

注意 这里是小端模式存储的,所以是下面的顺序。

16进制: 0x 00 00 00 00 00 00 00 01
(64位) 2进制:00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

对照上面表格,最后三位 0 01 就是表示无锁状态。


我们将代码中加入锁,再看下类布局:

public class MyClassLayout {
    public static void main(String[] args) {
        MyClassLayout myClassLayout = new MyClassLayout();
        synchronized (myClassLayout) {
            System.out.println(ClassLayout.parseInstance(myClassLayout).toPrintable());
        }
    }
}

执行结果:

demo.MyClassLayout object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           a8 f4 df e7 (10101000 11110100 11011111 11100111) (-404753240)
      4     4        (object header)                           5a 00 00 00 (01011010 00000000 00000000 00000000) (90)
      8     4        (object header)                           05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

上述结果中,最后三位是:000 表示轻量级锁。


我们多个线程访问一个资源时,我们判断是谁抢占来锁,一定要做出标记,来记录锁的持有者。
加锁一定会带来性能开销。所以为了优化,设计了多种性能开销不同的锁。
对于这些锁,各自有什么特性,我们后续再讲。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值