Synchronized加锁原理

synchronized 关键字,用来控制线程同步的,保证我们的线程在多线程的环境下,不被多个线程同时执行,确保我们数据的完整性。

同步方法和同步代码块操作:

private synchronized void add(){
        count ++;
    }
private void add(){
        synchronized (lock){
            count ++;
        }
    }

同步方法中默认使用的是this或者当前类对象作为锁;是由于java的每一个对象都有一个内置锁,用synchronized修饰的方法,内置锁会保护整个方法。在调用方法之前需要获取到这个内置锁,否则就会处于阻塞的状态。

注意:synchronized修饰静态方法,将锁住整个类。

同步代码块中可以选择使用对象来作为锁,灵活性就比较的大,可以是类锁、static修饰的对象锁、普通对象锁;因此,synchronized(lock)是基于lock这个对象的生命周期来控制锁的粒度。

注意:加锁的关键在于锁对象,需要对多个线程共享。

在Java SE 1.6之前为重量级锁。在Java SE 1.6版本就已经对synchronized进行来各种的优化,因为在大部分的情况下,加锁的代码并不存在多线程的竞争,为了减少获得锁和释放锁带来的性能消耗而引入来偏向锁和轻量级锁。在synchronized中,锁存在四种状态,分别是:无锁、偏向锁、轻量级锁,重量级锁。锁的状态根据竞争的激烈程度来从低到高不断的升级。

可以看出同步关键在与lock对象,为什么lock对象可以作为同步锁呢?因此就需要知道对象在内存中是如何布局的

对象在内存中的布局

在JVM虚拟机(hotpot虚拟机)中,对象在内存中的布局可以分为三个区域:对象头(header)、实例数据(Instance Data)、对齐填充(Padding):

Jvm源码实现:

当我们使用new创建一个对象实例当时候,JVM层面实际上会创建一个instanceOopDesc对象。

Hotspot虚拟机采用OOP-Klass 模型来描述Java对象实例,OOP(Ordinary Object Point)指的是普通对象指针,Klass用来描述对象实例的具体类型。Hotspot采用instanceOopDesc和arrayOopDesc来描述对象头,arrayOopDesc对象来描述数组类型。

#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

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

从以上源码中可以看出InstanceOopDesc继承了OopDesc,并且在普通实例对象中,创建了_mark和_metadata两个成员。

class markOopDesc: public oopDesc {
 private:
  // Conversion
  uintptr_t value() const { return (uintptr_t) this; }

 public:
  // Constants
  enum { age_bits                 = 4,
         lock_bits                = 2,
         biased_lock_bits         = 1,
         max_hash_bits            = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
         hash_bits                = max_hash_bits > 31 ? 31 : max_hash_bits,
         cms_bits                 = LP64_ONLY(1) NOT_LP64(0),
         epoch_bits               = 2
  };
}

_mark的类型为markOop,表示对象标记;

_metadata 表示类元信息,类元信息中存储的是对象指向它的类元数据Klass 的首地址,_klass表示普通指针,_compressed_klass表示压缩类指针。

Marker word 记录类对象和锁有关的信息,当某个对象被synchronized关键字当成同步锁的时候,围绕这个锁的一系列的操作都和Mark word有关。Mark word在32位的虚拟机中的长度为32bit,在64位的虚拟机中的长度为64bit。

Mark word 里面存储的数据会随着锁标志位的变化而变化,它存储的数据可能存在以下5种情况:

为什么任何的对象都可以实现锁?

1、Java中的每个对象都派生自Object类,而每个Java Object 在JVM内部都有一个native的C++对象oop/oopDesc进行对应。

2、线程在获取锁的时候,实际上就是获得一个监视器对象monitor(可以认为是一个同步对象),所有的Java对象是天生携带monitor,在多个线程访问同步代码块的时候,相当于去争抢对象的监视器,修改对象中锁标识。

ObjectMonitor* monitor() const {
    assert(has_monitor(), "check");
    // Use xor instead of &~ to provide one extra tag-bit check.
    return (ObjectMonitor*) (value() ^ monitor_value);
  }

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的锁(Lock)是用于实现线程同步的机制,确保多个线程之间对共享资源的访问是安全的。Lock接口定义了对共享资源的访问控制方法,常见的实现类有ReentrantLock和ReentrantReadWriteLock。 锁实现原理: 在Java中,锁的实现主要依赖于底层的操作系统提供的原语,如CAS(Compare and Swap)或Mutex。CAS是一种乐观锁机制,通过比较并交换的方式来实现对共享资源的访问控制。Mutex是一种互斥锁机制,通过对共享资源进行加锁和解锁操作来实现线程之间的同步。 动态代理: 动态代理是一种在运行时生成代理对象的机制,可以在不修改原始类的情况下为其添加额外的功能。Java中的动态代理主要依赖于两个接口:InvocationHandler和Proxy。InvocationHandler定义了代理对象要执行的方法,而Proxy则负责生成代理对象。 反射: 反射是Java中一种强大的机制,它允许程序在运行时获取类的信息并操作类或对象。通过反射,可以动态地创建对象、调用方法、访问字段等。Java中的反射主要依赖于Class类和java.lang.reflect包中的其他类。 总结: Java中的锁机制(Lock)是实现线程同步的重要工具,它依赖于底层的操作系统提供的原语。动态代理是一种在运行时生成代理对象的机制,可以为原始类添加额外的功能。而反射是Java中一种强大的机制,允许程序在运行时获取类的信息并操作类或对象。这些特性都为Java提供了更高级别的编程能力和灵活性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值