精华内容
下载资源
问答
  • java

    千次阅读 2018-10-23 23:39:22
    Java对象保存在内存中时,由以下三部分组成: 1,对象头 2,实例数据 3,对齐填充字节 一,对象头 java的对象头由以下三部分组成: 1,Mark Word 2,指向类的指针 3,数组长度(只有数组对象才有)   1,...

    目录

    一,对象头

    1,Mark Word

    2,指向类的指针

    3,数组长度

    二,实例数据

    三,对齐填充字节

    Java对象保存在内存中时,由以下三部分组成:

    1,对象头

    2,实例数据

    3,对齐填充字节

    一,对象头
    java的对象头由以下三部分组成:

    1,Mark Word

    2,指向类的指针

    3,数组长度(只有数组对象才有)

     

    1,Mark Word
    Mark Word记录了对象和锁有关的信息,当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关。

    Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

    Mark Word在不同的锁状态下存储的内容不同,在32位JVM中是这么存的:

    锁状态

    25bit

    4bit

    1bit

    2bit

    23bit

    2bit

    是否偏向锁

    锁标志位

    无锁

    对象的HashCode

    分代年龄

    0

    01

    偏向锁

    线程ID

    Epoch

    分代年龄

    1

    01

    轻量级锁

    指向栈中锁记录的指针

    00

    重量级锁

    指向重量级锁的指针

    10

    GC标记

    11

    其中无锁和偏向锁的锁标志位都是01,只是在前面的1bit区分了这是无锁状态还是偏向锁状态。

    JDK1.6以后的版本在处理同步锁时存在锁升级的概念,JVM对于同步锁的处理是从偏向锁开始的,随着竞争越来越激烈,处理方式从偏向锁升级到轻量级锁,最终升级到重量级锁。

     

    JVM一般是这样使用锁和Mark Word的:

    1,当没有被当成锁时,这就是一个普通的对象,Mark Word记录对象的HashCode,锁标志位是01,是否偏向锁那一位是0。

    2,当对象被当做同步锁并有一个线程A抢到了锁时,锁标志位还是01,但是否偏向锁那一位改成1,前23bit记录抢到锁的线程id,表示进入偏向锁状态。

    3,当线程A再次试图来获得锁时,JVM发现同步锁对象的标志位是01,是否偏向锁是1,也就是偏向状态,Mark Word中记录的线程id就是线程A自己的id,表示线程A已经获得了这个偏向锁,可以执行同步锁的代码。

    4,当线程B试图获得这个锁时,JVM发现同步锁处于偏向状态,但是Mark Word中的线程id记录的不是B,那么线程B会先用CAS操作试图获得锁,这里的获得锁操作是有可能成功的,因为线程A一般不会自动释放偏向锁。如果抢锁成功,就把Mark Word里的线程id改为线程B的id,代表线程B获得了这个偏向锁,可以执行同步锁代码。如果抢锁失败,则继续执行步骤5。

    5,偏向锁状态抢锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁Mark Word的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把Mark Word中的锁标志位改成00,可以执行同步锁代码。如果保存失败,表示抢锁失败,竞争太激烈,继续执行步骤6。

    6,轻量级锁抢锁失败,JVM会使用自旋锁,自旋锁不是一个锁状态,只是代表不断的重试,尝试抢锁。从JDK1.7开始,自旋锁默认启用,自旋次数由JVM决定。如果抢锁成功则执行同步锁代码,如果失败则继续执行步骤7。

    7,自旋锁重试之后如果抢锁依然失败,同步锁会升级至重量级锁,锁标志位改为10。在这个状态下,未抢到锁的线程都会被阻塞。

     

    2,指向类的指针
    该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

    Java对象的类数据保存在方法区。

     

    3,数组长度
    只有数组对象保存了这部分数据。

    该数据在32位和64位JVM中长度都是32bit。

     

    二,实例数据
    对象的实例数据就是在java代码中能看到的属性和他们的值。

     

    三,对齐填充字节
    因为JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数,没有特别的功能。

     

    以上。
    --------------------- 
    作者:lkforce 
    来源:CSDN 
    原文:https://blog.csdn.net/lkforce/article/details/81128115 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • java锁机制Synchronized java锁机制Synchronized java锁机制Synchronized java锁机制Synchronized
  • Java锁机制详解.pdf

    2017-11-02 17:05:48
    Java锁机制详解.pdf java线程 java多线程 Java锁机制详解.pdf java线程 java多线程
  • Java锁升级

    千次阅读 多人点赞 2020-04-06 01:36:52
    Java锁升级 对象内存布局 Java对象在内存中存储的布局可以分为3块区域: 对象头、实例数据、对齐填充。 对象头,分为两个部分,第一个部分存储对象自身的运行时数据,又称为Mark Word,32位虚拟机占32bit,64位虚拟机占...

    Java锁

    锁概念

    公平锁与非公平锁

    公平锁:线程获取锁的顺序与其申请顺序一致

    非公平锁:线程获取锁的顺序并不是按照申请锁顺序,有可能后申请锁的线程先获取锁。

    可重入锁与不可重入锁

    可重入锁:线程获取锁后,可以重复进入同步代码区,不需要重复申请锁

    不可重入锁:线程每次进入同步代码区时,均需要获取锁,即使已经获取过该锁。

    悲观锁与乐观锁

    悲观锁:总是持有悲观的态度,认为并发冲突一般会发生

    乐观锁:总是持有乐观的态度,认为并发冲突一般不会发生

    锁消除与锁粗化

    锁消除:消除是Java虚拟机在JIT编译期间,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁时间。

    锁粗化:在遇到一连串地对同一锁不断进行请求和释放的操作时,把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数

    自旋锁和自适应自旋

    自旋锁:线程尝试获取锁,发现被占用后,不放弃cpu执行时间,进入阻塞状态,而是进入进行循环重试,重试指定次数后,仍未获取锁,则进入阻塞过程。

    自适应自旋:重试次数不固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。

    Java锁升级

    对象内存布局

    Java对象在内存中存储的布局可以分为3块区域: 对象头实例数据对齐填充

    对象头,分为两个部分,第一个部分存储对象自身的运行时数据,又称为Mark Word,32位虚拟机占32bit,64位虚拟机占64bit。如图所示,不同锁状态下,Mark Word的结构,理解下面要介绍的各种锁,和锁升级过程,都需要先充分了解Mark Word的结构。

    第二部分是类型指针,指向类元数据指针,虚拟机通过此指针,确定该对象属于那个类的实例。

    偏向锁

    引入偏向锁的目的是在没有多线程竞争的前提下,进一步减少线程同步的性能消耗。

    • 偏向锁的获取

      开启偏向锁模式后,锁第一次被线程获取的时候,虚拟机会把对象头中是否为偏向锁的标志位设位1,

      同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中。当有另外一个线程去尝试获取这个锁时, 偏向模式就宣告结束。

      根据锁对象目前是否处于被锁定的状态, 撤销偏向( Revoke Bias) 后恢复到未锁定( 标志位为“01”)或轻量级锁定( 标志位为“00”) 的状态

    • 偏向锁的释放

      偏向锁,并没有显式的锁释放过程,主要依靠锁的批量再偏向(Bulk Rebias)机制实现锁释放。

      该机制的主要工作原理如下:

      • 引入一个概念 epoch, 其本质是一个时间戳 , 代表了偏向锁的有效性,从前文描述的对象头结构中可以看到,

        epoch 存储在可偏向对象的 MarkWord 中。

      • 除了对象中的 epoch, 对象所属的类 class 信息中, 也会保存一个 epoch 值,每当遇到一个全局安全点时, 如果要对 class 进行批量再偏向, 则首先对 class 中保存的 epoch 进行增加操作, 得到一个新的 epoch_new

      • 然后扫描所有持有 class 实例的线程栈,根据线程栈的信息判断出该线程是否锁定了该对象, 仅将epoch_new 的值赋给被锁定的对象中。

      • 退出安全点后, 当有线程需要尝试获取偏向锁时, 直接检查 class中存储的 epoch 值是否与目标对象中存储的 epoch 值相等,如果不相等, 则说明该对象的偏向锁已经无效了, 可以尝试对此对象重新进行偏向操作。

    轻量级锁

    轻量级锁是相对于重量级锁(Synchrnoized)而言的,本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

    • 轻量级锁的获取
      • 线程进入同步块时,如果此同步对象没有被锁定(即锁标志位为01,是否为偏向锁为0),虚拟机在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的一个Mark Word的copy ,ower设置为当前线程。

      • 然后虚拟机使用CAS操作,尝试将Mark World更新为指向Lock Record的指针,如果更新成功,那么线程拥有了该对象的锁,并且将锁标志位置位00,如图所示

      • 如果只有一个线程尝试获取轻量级锁,会进行自旋等待,一旦有两条以及以上的线程抢占该锁,轻量级锁会升级为重量级锁。

        锁标志位置为10,Mark Word存储的就是指向重量级锁的指针

    • 轻量级锁释放
      • 如果对象的Mark Word仍然指向着线程的锁记录, 那就用CAS操作把对象当前的Mark Word和线程中复制的Displaced Mark Word替换回来, 如果替换成功, 整个同步过程就完成了。如果替换失败, 说明有其他线程尝试过获取该锁,轻量级锁膨胀为重量级锁,那就要在释放锁的同时, 唤醒被挂起的线程。

    整个锁升级过程

    参考文章

    偏向锁,轻量级锁与重量级锁的区别与膨胀

    Java中的偏向锁,轻量级锁, 重量级锁解析

    展开全文
  • Java锁机制

    千次阅读 多人点赞 2019-06-20 12:57:09
    Java锁的划分 Java锁具体可分为悲观锁/乐观锁、自旋锁/适应性自旋锁、偏向锁、轻量级锁/重量级锁、公平锁和非公平锁、可重入锁/非可重入锁、共享锁/排他锁 具体划分如下: 乐观锁VS悲观锁 特征 对于同一个...

    Java锁的划分

    Java锁具体可分为悲观锁/乐观锁、自旋锁/适应性自旋锁、偏向锁、轻量级锁/重量级锁、公平锁和非公平锁、可重入锁/非可重入锁、共享锁/排他锁
    具体划分如下:
    在这里插入图片描述

    乐观锁VS悲观锁

    概念

    • 对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Java中,synchronized关键字和Lock的实现类都是悲观锁。
    • 乐观锁认为自己在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据(可以使用版本号机制和CAS算法实现)。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)

    在这里插入图片描述

    应用场景

    • 悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。
    1. 传统的关系型数据库使用了很多悲观锁机制如行锁,表锁都是操作之前先上锁
    2. Java中的synchronized和ReentrantLock体现的就是悲观锁
    • 乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升
    1. java.util.concurrent.atomic包下面的原子变量类就是基于CAS实现的乐观锁

    优缺点

    乐观锁缺点

    1. ABA问题
      如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。
      J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题, 它可以通过控制变量值的版本来保证 CAS 的正确性。 大部分情况下 ABA 问题不会影响程序并发的正确性, 如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。

    2. 自旋时间长开销大
      自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。 如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用, 第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源, 延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。 第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation) 而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。

    3. 只能保证一个共享变量的原子操作 CAS只对单个共享变量有效,当操作涉及跨多个共享变量时CAS无效。 但是从 JDK 1.5开始,提供了AtomicReference类来保证引用对象之间的原子性, 可以把多个变量封装成对象里来进行 CAS 操作. 所以我们可以使用锁或者利用AtomicReference类把多个共享变量封装成一个共享变量来操作。

    示例

    // ------------------------- 悲观锁的调用方式 -------------------------
    // synchronized
    public synchronized void testMethod() {
    	// 操作同步资源
    }
    // ReentrantLock
    private ReentrantLock lock = new ReentrantLock(); // 需要保证多个线程使用的是同一个锁
    public void modifyPublicResources() {
    	lock.lock();
    	// 操作同步资源
    	lock.unlock();
    }
    
    // ------------------------- 乐观锁的调用方式 -------------------------
    private AtomicInteger atomicInteger = new AtomicInteger();  // 需要保证多个线程使用的是同一个AtomicInteger
    atomicInteger.incrementAndGet(); //执行自增1
    
    • 悲观锁显式进行锁定只同步操作

    自旋锁 VS 适应性自旋锁

    自旋锁概念

    • 阻塞或者唤醒一个JAVA的线程需要操作系统切换CPU状态来完成,这种状态的转换需要耗费处理器时间。如果同步代码块中的内容过于简单,很可能导致状态转换消耗的时间比用户代码执行的时间还要长。所以在短暂的等待之后就可以继续进行的线程,为了让线程等待一下,需要让线程进行自旋,在自旋完成之后,前面锁定了同步资源的线程已经释放了锁,那么当前线程就可以不需要阻塞便直接获取同步资源,从而避免了线程切换的开销。这就是自旋锁。
      在这里插入图片描述

    在这里插入图片描述

    自旋锁优缺点

    • 优点: 阻塞或唤醒一个 Java 线程需要操作系统切换 CPU 状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长。所以自旋锁就可以避免 CPU 切换带来的性能、时间等影响。
    • 缺点: 自旋等待虽然避免了线程切换的开销,但它要占用处理器时间。如果锁被占用的时间很短,自旋等待的效果就会非常好。反之,如果锁被占用的时间很长,那么自旋的线程只会白浪费处理器资源。所以,自旋等待的时间必须要有一定的限度,如果自旋超过了限定次数(默认是 10 次,可以使用 -XX:PreBlockSpin 来更改)没有成功获得锁,就应当挂起线程

    适应性自旋锁

    • 自适应意味着自旋的时间(次数)不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也是很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。如果对于某个锁,自旋很少成功获得过,那在以后尝试获取这个锁时将可能省略掉自旋过程,直接阻塞线程,避免浪费处理器资源。

    示例

    • AtomicInteger 是基于 CAS 实现的自旋锁
    // JDK 8
    // AtomicInteger 自增方法
    public final int incrementAndGet() {
      return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
    // Unsafe.class
    public final int getAndAddInt(Object var1, long var2, int var4) {
      int var5;
      do {
          var5 = this.getIntVolatile(var1, var2);
      } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
      return var5;
    }
    

    无锁VS偏向锁

    无锁

    • 无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。
    • 无锁的特点就是修改操作在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。CAS原理及应用即是无锁的实现。无锁无法全面代替有锁,但无锁在某些场合下的性能是非常高的。

    偏向锁

    • 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。
    • 在大多数情况下,锁总是由同一线程多次获得,不存在多线程竞争,所以出现了偏向锁。其目标就是在只有一个线程执行同步代码块时能够提高性能。
    • 在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。
    • 偏向锁在JDK 6及以后的JVM里是默认启用的。可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,关闭之后程序默认会进入轻量级锁状态。

    偏向锁的实现

    偏向锁的获取

    1. 访问Mark Word中偏向锁的标识是否设置成1,锁标志位是否为01,确认为可偏向状态。
    2. 如果为可偏向状态,则测试线程ID是否指向当前线程,如果是,进入步骤5,否则进入步骤3。
    3. 如果线程ID并未指向当前线程,则通过CAS操作竞争锁。如果竞争成功,则将Mark Word中线程ID设置为当前线程ID,然后执行5;如果竞争失败,执行4。
    4. 如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点(safepoint)时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。(撤销偏向锁的时候会导致stop the word)
    5. 执行同步代码。

    偏向锁的释放

    * 偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动去释放偏向锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

    轻量级锁VS重量级锁

    概念

    • 当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。

    轻量锁的加锁过程

    1. 在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方称之为 Displaced Mark Word
    2. 拷贝对象头中的Mark Word复制到锁记录中
    3. 拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,并将Lock record里的owner指针指向object mark word。如果更新成功,则执行步骤4,否则执行步骤5
    4. 如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态
    5. 如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 而当前线程便尝试使用自旋来获取锁,自旋就是为了不让线程阻塞,而采用循环去获取锁的过程

    公平锁VS非公平锁

    概念

    • 公平锁(Fair):加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得
    • 非公平锁(Nonfair):加锁时不考虑排队等待问题,直接尝试获取锁,如果的锁的状态可用,那么该线程会跳过所有等待直接获取锁,相当于有插队行为

    优缺点

    • 公平锁CPU唤醒阻塞线程的开销比非公平锁大,恢复一个挂起的线程到线程真正的去运行存在严重延迟。假设线程A持有一个锁,并且线程B请求这个锁。由于锁被A持有,因此B将被挂起。当A释放锁时,B将被唤醒,因此B会再次尝试获取这个锁。与此同时,如果线程C也请求这个锁,那么C很可能会在B被完全唤醒之前获得、使用以及释放这个锁。这样就是一种双赢的局面:B获得锁的时刻并没有推迟,C更早的获得了锁,并且吞吐量也提高了
    • 当持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁。在这些情况下,插队带来的吞吐量提升(当锁处于可用状态时,线程却还处于被唤醒的过程中)可能不会出现。

    示例

    • ReentrantLock 默认的lock()方法采用的是非公平锁
        /**
         * Creates an instance of {@code ReentrantLock} with the
         * given fairness policy.
         *
         * @param fair {@code true} if this lock should use a fair ordering policy
         */
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    
    • 公平锁有个!hasQueuedPredecessors()条件,意思是说当前同步队列没有前驱节点(也就是没有线程在等待)时才会去compareAndSetState(0, acquires)使用CAS修改同步状态变量。所以就实现了公平锁,根据线程发出请求的顺序获取锁
        static final class FairSync extends Sync {
            private static final long serialVersionUID = -3000897897090466540L;
    
            final void lock() {
                acquire(1);
            }
    
            /**
             * Fair version of tryAcquire.  Don't grant access unless
             * recursive call or no waiters or is first.
             */
            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0)
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                return false;
            }
        }
    
    • 非公平锁的实现在刚进入lock方法时会直接使用一次CAS去尝试获取锁,不成功才会到acquire方法,而在nonfairTryAcquire方法中并没有判断是否有前驱节点在等待,直接CAS尝试获取锁
    	  /**
    	 * Performs lock.  Try immediate barge, backing up to normal
    	 * acquire on failure.
    	 */
    	final void lock() {
    		if (compareAndSetState(0, 1))
    			setExclusiveOwnerThread(Thread.currentThread());
    		else
    			acquire(1);
    	}
        public final void acquire(int arg) {
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
        protected final boolean tryAcquire(int acquires) {
              return nonfairTryAcquire(acquires);
        }
     final boolean nonfairTryAcquire(int acquires) {
    	final Thread current = Thread.currentThread();
    	int c = getState();
    	if (c == 0) {
    		if (compareAndSetState(0, acquires)) {
    			setExclusiveOwnerThread(current);
    			return true;
    		}
    	}
    	else if (current == getExclusiveOwnerThread()) {
    		int nextc = c + acquires;
    		if (nextc < 0) // overflow
    			throw new Error("Maximum lock count exceeded");
    		setState(nextc);
    		return true;
    	}
    	return false;
    }
    

    可重入锁 VS 非可重入锁

    概念

    • 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁
    • 不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞
    • 不可重入锁,可能存在被当前线程所持有,且无法释放的死锁问题

    独享锁VS共享锁

    • 独享锁:该锁每一次只能被一个线程所持有,也叫排他锁,获得排他锁的 线程既能读数据又能写数据
    • 共享锁:该锁可被多个线程共有,典型的就是ReentrantReadWriteLock里的读锁,它的读锁是可以被共享的,但是它的写锁确每次只能被独占
    • 独享锁与共享锁时通过AQS来实现 的

    示例

    • 独享锁ReentrantLock源码中可以看出无论是公平锁还是非公平锁,只要当线程不是拥有锁的线程,都不能加锁
     final boolean nonfairTryAcquire(int acquires) {
    	final Thread current = Thread.currentThread();
    	int c = getState();
    	if (c == 0) {
    		if (compareAndSetState(0, acquires)) {
    			setExclusiveOwnerThread(current);
    			return true;
    		}
    	}
    	else if (current == getExclusiveOwnerThread()) {
    		int nextc = c + acquires;
    		if (nextc < 0) // overflow
    			throw new Error("Maximum lock count exceeded");
    		setState(nextc);
    		return true;
    	}
    	return false;
    }
    
    • 共享锁tryAcquireShared(int unused)方法中,如果其他线程已经获取了写锁,则当前线程获取读锁失败,进入等待状态.如果当前线程获取了写锁或者写锁未被获取,则当前线程(线程安全,依靠CAS保证)增加读状态,成功获取读锁。读锁的每次释放(线程安全的,可能有多个读线程同时释放读锁)均减少读状态,减少的值是“1<<16”。所以读写锁才能实现读读的过程共享,而读写、写读、写写的过程互斥

    • protected final int tryAcquireShared(int unused) {
      Thread current = Thread.currentThread();
      int c = getState();
      if (exclusiveCount© != 0 &&
      getExclusiveOwnerThread() != current)
      return -1;
      int r = sharedCount©;
      if (!readerShouldBlock() &&
      r < MAX_COUNT &&
      compareAndSetState(c, c + SHARED_UNIT)) {
      if (r == 0) {
      firstReader = current;
      firstReaderHoldCount = 1;
      } else if (firstReader == current) {
      firstReaderHoldCount++;
      } else {
      HoldCounter rh = cachedHoldCounter;
      if (rh == null || rh.tid != getThreadId(current))
      cachedHoldCounter = rh = readHolds.get();
      else if (rh.count == 0)
      readHolds.set(rh);
      rh.count++;
      }
      return 1;
      }
      return fullTryAcquireShared(current);
      }

    引申

    1. CAS原理
      https://blog.csdn.net/weixin_41950473/article/details/93994332
    2. AQS原理
    3. ReentrantLock锁
    4. 数据库的乐观锁和悲观锁
    5. Synchronized的底层优化
    6. ReentrantReadWriteLock原理及读锁与写锁

    参考

    展开全文
  • java锁消除和锁粗化

    万次阅读 2020-06-09 16:37:49
    什么是java锁消除呢?其实这些都是JVM帮我们做的功能,在JVM判断一个锁不会被其他线程使用,就会把锁消除来提高性能。 比如下面代码,我们知道StringBuffer的append方法上加入了synchronized关键字,所以是加了锁的...

    什么是java锁消除呢?其实这些都是JVM帮我们做的功能,在JVM判断一个锁不会被其他线程使用,就会把锁消除来提高性能。

    比如下面代码,我们知道StringBuffer的append方法上加入了synchronized关键字,所以是加了锁的,但是JVM知道这个StringBuffer是线程私有的,所以没有其他线程会访问到,所以JVM会取消锁,加快性能。

        private void appendString(){
    
            StringBuffer sb = new StringBuffer();
            sb.append("a").append("b").append("c");
    
        }

     

    锁粗化:

    下面代码在一个循环里加上synchronized,但是每次上锁和取消锁也很耗费性能,JVM会进行优化,在while前面加入synchronized关键字变成了下面的效果,这样就达到了锁粗化,提高性能。

        private void lockCoarsening(){
            int i = 0;
            while (i<1000){
                synchronized (Object.class){
                    // do something
                    i++;
                }
            }
        }
    
    
        private void lockCoarsening() {
            int i = 0;
            synchronized (Object.class) {
                while (i < 1000) {
                    // do something
                    i++;
                }
            }
        }

     

    学习记录,如果有什么不正确的地方还望指正。

    展开全文
  • Java锁的种类

    千次阅读 2019-04-17 16:55:21
    Java锁的种类 内置锁 Java 提供了一种内置的锁机制来支持原子性:同步代码块(Synchronized Block)。同步代码块包含两部分,一个作为锁的对象引用,一个作为由这个锁保护的代码块。以synchronized来修饰的方法就是一...
  • Java锁消除和锁粗化

    万次阅读 多人点赞 2018-06-02 12:29:49
    转载自:Java锁消除非商业转载,可联系本人删除概述锁消除是Java虚拟机在JIT编译是,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁时间。实验看如下代码:package...
  • Java锁之公平和非公平锁

    千次阅读 2019-05-27 21:34:26
    Java锁之公平和非公平锁 目录 公平锁和非公平锁概念 公平锁和非公平锁区别 ReentrantLock和synchronized是公平锁还是非公平锁? 1. 公平锁和非公平锁概念 公平锁:是指多个线程按照申请锁的顺序来...
  • 从并发概念、场景分析出发,依次引出锁、等待队列等概念,直至分析清楚java锁机制实现的原理。并以java锁机制实现基类AbstractQueuedSynchronizer的实现为例,从类(核心属性、方法)设计思路,到对关键代码做注释...
  • 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁乐观锁 VS 悲观锁1.乐观锁2.悲观锁3.总之公平锁 VS 非公平锁1.公平锁2.非公平锁3.典型应用独享锁 VS 共享锁1.独享锁2.共享锁3.比较4.AQS分段锁Java线程...
  • java锁升级过程

    万次阅读 多人点赞 2020-03-14 20:32:17
    java中对象有4种状态:(级别从低到高) 1.无锁状态 2.偏向状态 3.轻量级状态 4.重量级状态 对象头分两部分信息,第一部分用于存储哈希码、GC分代年龄等,这部分数据被称为"Mark Word"。在32位的HotSpot...
  • 面试必会系列 - 1.5 Java 机制

    万次阅读 2020-09-04 12:25:48
    Java 机制 概览 syncronized 升级过程 ReentrantLock 可重入 volatile 关键字 JUC 包下新的同步机制 syncronized 给一个变量/一段代码加锁,线程拿到之后,才能修改一个变量/执行一段代码 wait() notify...
  • 转载自 java并发编程之4——Java锁分解锁分段技术   并发编程的所有问题,最后都转换成了,“有状态bean”的状态的同步与互斥修改问题。而最后提出的解决“有状态bean”的同步与互斥修改问题的方案是为所有修改...
  • Java锁消除

    千次阅读 2015-06-05 14:38:23
    概述消除是Java虚拟机在JIT编译是,通过对运行上下文的扫描,去除不可能存在共享资源竞争的,通过消除,可以节省毫无意义的请求时间。实验看如下代码:package com.winwill.lock;/** * @author qifuguang ...
  • Java锁详解

    千次阅读 多人点赞 2019-06-29 22:57:22
    文章目录什么是锁锁的实现方式涉及的几个重要概念类和对象(重要)synchronized实现原理 什么是 计算机还是单线程的时代,下面代码中的count,始终只会被一个线程累加,调用addOne()10次,count的值一定就...
  • Java锁升级过程

    千次阅读 2019-10-12 10:31:14
    因为经过HotSpot的作者大量的研究发现,大多数时候是不存在竞争的,常常是一个线程多次获得同一个,因此如果每次都要竞争会增大很多没有必要付出的代价,为了降低获取的代价,才引入的偏向。 轻量级 ...
  • java锁总结

    千次阅读 2018-06-01 17:20:31
    在介绍java锁之前,先说下什么是markword,markword是java对象数据结构中的一部分,要详细了解java对象的结构可以 点击这里 ,这里只做markword的详细介绍,因为对象的markword和java各种类型的锁密切相关; markword...
  • Java 的种类

    千次阅读 2018-04-21 12:25:33
    上一篇既然提到了,这一篇来详细介绍JAVA中的,也为之后JUC下的做一个铺垫 其实如果按照名称来说,大概有以下名词: 自旋 ,自旋的其他种类,阻塞,可重入 ,读写 ,互斥 ,悲观 ,乐观 ...
  • java锁的种类以及辨析

    千次阅读 2014-10-14 17:48:12
    java锁的种类以及辨析(一):自旋锁 http://ifeve.com/java_lock_see1/
  • java 内存模型 java ReentrantLock-07

    万次阅读 2018-12-12 21:02:20
    java 并发编程中最重要的同步机制。 除了让临界区互斥执行外,还可以让释放的线程向获取同一个的线程发送消息。 实例 MonitorExample.java class MonitorExample { int a = 0; public synchronized ...
  • 一、什么是? 在计算机科学中,(lock)或互斥(mutex)是一种同步机制,用于在有许多执行线程的环境中强制对资源的访问限制。旨在强制实施互斥排他、并发控制策略。 通常需要硬件支持才能有效实施。这种...
  • JDK1.6为了减少获得和释放所带来的性能消耗,引入了“偏向”和“轻量级”,所以在JDK1.6里一共有四种状态,无锁状态,偏向状态,轻量级状态和重量级状态,它会随着竞争情况逐渐升级。可以升级但不...
  • JAVA锁机制

    千次阅读 2017-01-25 17:26:19
    synchronized和Lock均为可重入。即可为该对象多次加锁,通过标志+1进行操作;当所标志为0时,释放所。重入目的是为防止死锁发生。 synchronized:基于系统内核实现线程等待(通过linux系统pthread_...
  • JAVA锁的膨胀过程

    千次阅读 2019-07-18 11:25:52
    Java 语言中,使用 Synchronized 是能够实现线程同步的,即加锁。并且实现的是悲观,在操作同步资源的时候直接先加锁。 加锁可以使一段代码在同一时间只有一个线程可以访问,在增加安全性的同时,牺牲掉的是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 561,444
精华内容 224,577
关键字:

java锁

java 订阅