精华内容
下载资源
问答
  • 因为项目有个结算金额的情况,在0点时会统计各个...查看那些sql锁表,那些表锁了。解决。开发重写代码。将调用到锁表的内容都替换成其他参数。如之前user表里的字段user变成user_id。因为APP登陆需要数据库user表。...

    因为项目有个结算金额的情况,在0点时会统计各个用户的金额,在统计期间会造成user表锁表。所以用户到这个统计期间无法登陆APP。

    排查。

    show engine innodb status\G;

    show processlist;

    查看那些sql锁表,那些表锁了。

    解决。开发重写代码。将调用到锁表的内容都替换成其他参数。如之前user表里的字段user变成user_id。因为APP登陆需要数据库user表。所以重写后,不会在锁这个表

    展开全文
  • Java锁升级

    千次阅读 2018-12-07 16:44:15
    Java SE1.6为了减少获得和释放所带来的性能消耗,引入了“偏向”和“轻量级”,所以在Java SE1.6里一共有四种状态,无锁状态,偏向状态,轻量级状态和重量级状态,它会随着竞争情况逐渐升级。可以...

    前言:对象的对象头中的存放着synchronized用的锁信息,详细结构内容请看《Java对象的内存布局

    锁的升级

    Java SE1.6为了减少获得锁和释放锁所带来的性能消耗,引入了“偏向锁”和“轻量级锁”,所以在Java SE1.6里锁一共有四种状态,无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,它会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率,下文会详细分析。

     

    偏向锁

    Hotspot的作者经过以往的研究发现大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要花费CAS操作来加锁和解锁,而只需简单的测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁,如果测试成功,表示线程已经获得了锁,如果测试失败,则需要再测试下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁),如果没有设置,则使用CAS竞争锁,如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

    偏向锁获取过:

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

    偏向锁的撤销:偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态,如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程。

    下图中的线程1演示了偏向锁初始化的流程,线程2演示了偏向锁撤销的流程:

    偏向锁的撤销

    关闭偏向锁:偏向锁在Java 6和Java 7里是默认启用的,但是它在应用程序启动几秒钟之后才激活,如有必要可以使用JVM参数来关闭延迟-XX:BiasedLockingStartupDelay = 0。如果你确定自己应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数关闭偏向锁-XX:-UseBiasedLocking=false,那么默认会进入轻量级锁状态。

    轻量级锁

    轻量级锁加锁

    • 在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为“01”状态,且偏向锁标识位为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁记录目前的Mark Word的拷贝(称为Displaced Mark Word) 
    • 拷贝mark word的作用:为了不想在lock与unlock这种底层操作上再加同步。
    • 修改Object mark word轻量级锁指针作用:告诉其他线程,该object monitor已被占用
    • owner指向object mark word作用:在下面的运行过程中,识别哪个对象被锁住了。

    这时候堆栈与对象头的状态如下

    • 然后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位(Mark Word的最后2bit)将转变为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如下

    • 如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧。如果指向,说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明这个锁对象已经被其他线程抢占了。如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁,锁标志的状态值变为”10”,Mark Word中存储的就是指向重量级(互斥量)的指针。

    轻量级锁解锁:轻量级解锁时,会使用原子的CAS操作来将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。下图是两个线程同时争夺锁,导致锁膨胀的流程图。
    轻量级锁

    因为自旋会消耗CPU,为了避免无用的自旋(比如获得锁的线程被阻塞住了),一旦锁升级成重量级锁,就不会再恢复到轻量级锁状态。当锁处于这个状态下,其他线程试图获取锁时,都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮的夺锁之争。

    重量级锁

    重量锁在JVM中又叫对象监视器(Monitor),它很像C中的Mutex,除了具备Mutex(0|1)互斥的功能,它还负责实现了Semaphore(信号量)的功能,也就是说它至少包含一个竞争锁的队列,和一个信号阻塞队列(wait队列),前者负责做互斥,后一个用于做线程同步。

    锁的优缺点对比

    优点

    缺点

    适用场景

    偏向锁

    加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距。

    如果线程间存在锁竞争,会带来额外的锁撤销的消耗。

    适用于只有一个线程访问同步块场景。

    轻量级锁

    竞争的线程不会阻塞,提高了程序的响应速度。

    如果始终得不到锁竞争的线程使用自旋会消耗CPU。

    追求响应时间。

    同步块执行速度非常快。

    重量级锁

    线程竞争不使用自旋,不会消耗CPU。

    线程阻塞,响应时间缓慢。

    追求吞吐量。

    同步块执行速度较长。

     

    推荐阅读:从源码层面解释锁 https://www.aimoon.site/blog/2018/05/21/biased-locking/

    参考

    展开全文
  • Java锁消除

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

    概述

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

    实验

    看如下代码:

    package com.winwill.lock;
    
    /**
     * @author qifuguang
     * @date 15/6/5 14:11
     */
    public class TestLockEliminate {
        public static String getString(String s1, String s2) {
            StringBuffer sb = new StringBuffer();
            sb.append(s1);
            sb.append(s2);
            return sb.toString();
        }
    
        public static void main(String[] args) {
            long tsStart = System.currentTimeMillis();
            for (int i = 0; i < 1000000; i++) {
                getString("TestLockEliminate ", "Suffix");
            }
            System.out.println("一共耗费:" + (System.currentTimeMillis() - tsStart) + " ms");
        }
    }

    getString()方法中的StringBuffer数以函数内部的局部变量,进作用于方法内部,不可能逃逸出该方法,因此他就不可能被多个线程同时访问,也就没有资源的竞争,但是StringBuffer的append操作却需要执行同步操作:

        @Override
        public synchronized StringBuffer append(String str) {
            toStringCache = null;
            super.append(str);
            return this;
        }

    逃逸分析和锁消除分别可以使用参数-XX:+DoEscapeAnalysis和-XX:+EliminateLocks(锁消除必须在-server模式下)开启。使用如下参数运行上面的程序:

    -XX:+DoEscapeAnalysis -XX:-EliminateLocks

    得到如下结果:
    这里写图片描述

    使用如下命令运行程序:

    -XX:+DoEscapeAnalysis -XX:+EliminateLocks

    得到如下结果:
    这里写图片描述

    结论

    由上面的例子可以看出,关闭了锁消除之后,StringBuffer每次append都会进行锁的申请,浪费了不必要的时间,开启锁消除之后性能得到了提高。

    展开全文
  • 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

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

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

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

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

    万次阅读 2020-06-09 16:37:49
    什么是java锁消除呢?其实这些都是JVM帮我们做的功能,在JVM判断一个锁不会被其他线程使用,就会把锁消除来提高性能。 比如下面代码,我们知道StringBuffer的append方法上加入了synchronized关键字,所以是加了锁的...
  • 作者:张昌昌 1、问题描述  Erlang端通过odbc去写... 思路:利用适配器原理,适配erlang和java的数据库连接,让erlang端对数据的操作与java端对该数据的操作,分时序顺序进行,其中一端在进行写操作时上  
  • Java锁之公平和非公平锁

    千次阅读 2019-05-27 21:34:26
    Java锁之公平和非公平锁 目录 公平锁和非公平锁概念 公平锁和非公平锁区别 ReentrantLock和synchronized是公平锁还是非公平锁? 1. 公平锁和非公平锁概念 公平锁:是指多个线程按照申请锁的顺序来...
  • java锁总结

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

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

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

    万次阅读 2020-08-01 13:34:14
    一个大型的系统往往被分为几个子系统来做,一个子系统可以部署在一台机器的多个 JVM(java虚拟机) 上,也可以部署在多台机器上。但是每一个系统不是独立的,不是完全独立的。需要相互通信,共同实现业务功能。 一句...
  • java 偏向

    千次阅读 2016-05-08 19:05:57
    java 偏向  Java偏向(Biased Locking)是Java6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。 偏向,顾名思义,它会偏向于第一个访问的线程,如果在接下来的...
  • java 内存模型 java ReentrantLock-07

    万次阅读 2018-12-12 21:02:20
    java 并发编程中最重要的同步机制。 除了让临界区互斥执行外,还可以让释放的线程向获取同一个的线程发送消息。 实例 MonitorExample.java class MonitorExample { int a = 0; public synchronized ...
  • 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁乐观锁 VS 悲观锁1.乐观锁2.悲观锁3.总之公平锁 VS 非公平锁1.公平锁2.非公平锁3.典型应用独享锁 VS 共享锁1.独享锁2.共享锁3.比较4.AQS分段锁Java线程...
  • Java显式

    千次阅读 2019-11-23 18:33:37
    Java提供的关键字,属于Java语法层面的互斥,隐式,由JVM来实现加锁和释放。 优点 代码编写简单 可读性好 缺点 加锁和释放由JVM来完成,不够灵活。 获取的过程不可中断。 不支持超时,获取不到会一直...
  • Java中的

    万次阅读 多人点赞 2016-04-20 21:38:47
    在学习或者使用Java的过程中进程会遇到各种各样的的概念:公平、非公平、自旋、可重入、偏向、轻量级、重量级、读写、互斥等待。这里整理了Java中的各种,若有不足之处希望大家在下方留言探讨...
  • Java偏向

    千次阅读 2015-06-05 14:07:45
    概述偏向是JDK 1.6提出的一种优化方式,起核心思想是如果程序没有竞争,则取消之前已经取得的线程的同步操作。也就是说,某一个被一个线程获取之后,便进入了偏向模式,当该线程再次请求这个时,就无需...
  • 优化  自旋:非自旋在未获取的情况会被阻塞,之后再唤醒尝试获得。而JDK的阻塞和唤醒是基于操作系统实现的,会有系统资源的开销。自旋就是线程不停地循环尝试获得,而不会将自己阻塞,这样不会...
  • Java对象的理解

    万次阅读 热门讨论 2018-07-16 14:57:27
    Java 以前理解Java的锁机制认为:锁的类型分为‘类锁’,’方法锁‘,’对象锁‘。 1.类锁(synchronize(静态对象)):类的所有...以上都是在学习Java锁的基础理解 其实上面所有的锁,都是“对象锁” 类锁...
  • 阿里面试失败后,一气之下我图解了Java中18把

    万次阅读 多人点赞 2021-06-17 23:21:47
    升级(无锁|偏向|轻量级|重量级优化技术(粗化、消除) 乐观和悲观 悲观 悲观对应于生活中悲观的人,悲观的人总是想着事情往坏的方向发展。 举个生活中的例子,假设厕所只有一个...
  • 一、什么是? 在计算机科学中,(lock)或互斥(mutex)是一种同步机制,用于在有许多执行线程的环境中强制对资源的访问限制。旨在强制实施互斥排他、并发控制策略。 通常需要硬件支持才能有效实施。这种...
  • JAVA锁机制

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

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 544,607
精华内容 217,842
关键字:

java锁表

java 订阅