您现在的位置是:主页 > news > 做网站建设专业定制/付费推广方式有哪些

做网站建设专业定制/付费推广方式有哪些

admin2025/6/29 14:19:14news

简介做网站建设专业定制,付费推广方式有哪些,双重预防机制信息化平台,聊天软件出售深入理解并发编程之synchronize锁竞争偏向锁轻量锁重量锁原理分析 文章目录深入理解并发编程之synchronize锁竞争偏向锁轻量锁重量锁原理分析前言一、偏向锁偏向锁的过程偏向锁的撤销图解偏向锁疑惑点,为啥一个线程还用锁二、轻量级锁轻量级锁的竞争轻量级锁的释放轻…

做网站建设专业定制,付费推广方式有哪些,双重预防机制信息化平台,聊天软件出售深入理解并发编程之synchronize锁竞争偏向锁轻量锁重量锁原理分析 文章目录深入理解并发编程之synchronize锁竞争偏向锁轻量锁重量锁原理分析前言一、偏向锁偏向锁的过程偏向锁的撤销图解偏向锁疑惑点,为啥一个线程还用锁二、轻量级锁轻量级锁的竞争轻量级锁的释放轻…

深入理解并发编程之synchronize锁竞争偏向锁轻量锁重量锁原理分析

文章目录

  • 深入理解并发编程之synchronize锁竞争偏向锁轻量锁重量锁原理分析
  • 前言
  • 一、偏向锁
    • 偏向锁的过程
    • 偏向锁的撤销
    • 图解偏向锁
    • 疑惑点,为啥一个线程还用锁
  • 二、轻量级锁
    • 轻量级锁的竞争
    • 轻量级锁的释放
    • 轻量级锁说明
  • 三、重量级锁
    • 锁的升级图
  • 总结
    • 锁的优化
    • 锁的粗化
    • 锁的消除


前言

偏向锁: 同步代码块一直都是同一个线程获取锁。
轻量级锁:多个线程在间隔的形式获取锁,没有同时在竞争锁。
重量级锁:多个线程同时在竞争同一把锁。
在这里插入图片描述

一、偏向锁

看下面代码:

public class Test007 extends Thread {private static Object object = new Object();@Overridepublic void run() {for (int i = 0; i < 5; i++) {synchronized (object) {System.out.println(ClassLayout.parseInstance(object).toPrintable());}}}public static void main(String[] args) {Test007 t1 = new Test007();t1.start();;}
}

由于一直是一个线程在持有锁标记,此时使用的则为偏向锁,看标识位显示为01位偏向锁。
在这里插入图片描述
偏向锁正常是等待几秒才激活,可以使用下面设置让其立即生效:

-XX:BiasedLockingStartupDelay=0

偏向锁的过程

当一个线程访问同步代码块并获取锁时;会在 对象头 和 自己的栈帧中 的锁记录中记录存储偏向锁的线程ID;以后该线程再次进入同步代码块时不再需要CAS来加锁和解锁;只需要简单测试一下对象头的 mark word 中偏向锁线程的ID是否是当前线程ID;

  • 如果成功;表示线程已经获取到锁直接进入代码块运行,
  • 如果测试失败;检查当前偏向锁字段是否为0;
  • 如果为0(表示线程还不是偏向锁,是无锁状态);采用CAS操作将偏向锁字段设置为1;并且更新自己的线程ID到mark word 字段中;
  • 如果为1;表示此时偏向锁已经被别的线程获取;则此时线程不断尝试使用CAS获取偏向锁;
  • 或者将偏向锁撤销,升级成轻量级锁; (升级概率较大)

偏向锁的撤销

偏向锁使用一种等待竞争出现才释放锁的机制;当有其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放偏向锁, 偏向锁的撤销开销较大;需要等待线程进入全局安全点 safepoint。

  1. 偏向锁的撤销必须等待全局安全的点
  2. 暂停拥有偏向锁的线程,判断锁对象是否被锁定
  3. 撤销偏向锁,将对象头中的标记01恢复为00 轻量级锁或者重量级

图解偏向锁

在这里插入图片描述

如上图,假设只有线程T1的时候,第一次进来,对象头没有存储当前线程T1为空的,这时候修改对象头保存线程T1,执行同步代码块,当第二次执行的时候判断对象头有存储并且是T1线程,并且锁标记为偏向锁,修改对象头保存线程T1,然后执行同步代码块(这个锁会偏向于第一个获得它的线程,会在对象头存储锁偏向的线程ID,以后该线程进入和退出同步块时只需要检查是否为偏向锁、锁标志位以及ThreadID即可);假设后来加入了线程T2,同样检查,发现不是对象头不是T2,CAS几次自旋尝试修改为T2,几次修改失败后,则撤销了偏向锁(类似于T2线程尝试几次抓阄,结果都没抓到,认为有黑幕举报了)。
偏向锁适合于只有一个线程重复获取锁的时候,没有任何的竞争,从而提高锁的效率。

疑惑点,为啥一个线程还用锁

因为正常自己写的代码虽然一个线程不用加锁的,但是我们用的不是自己写的代码的时候,例如我们单个线程使用HashTable时候,HashTable本身就是加锁了的,这时候就是偏向锁了。

二、轻量级锁

还是上面的代码,这样使用:

public class Test007 extends Thread {private  Object object = new Object();@Overridepublic void run() {for (int i=0;i<5;i++){synchronized (object) {try {Thread.sleep(100); //保证交替执行} catch (InterruptedException e) {e.printStackTrace();}System.out.println(ClassLayout.parseInstance(object).toPrintable());}}}public static void main(String[] args) throws InterruptedException {new Test007().start();new Test007().start();}
}

线程1执行后进入休眠状态,然后线程2再执行,因为执行时间比较短,两个线程错开了同时竞争的时刻,这时是轻量级锁,此时标志位为00
在这里插入图片描述

轻量级锁的竞争

  1. 判断当前对象是否处于无锁状态,若是,则JVM首先将在当前线程的栈帧(每个线程独立的)中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝(官方把这份拷贝加了一个Displaced前缀,即Displaced Mark Word,这一步是为了释放锁的时候恢复本来的内容);否则执行步骤3;
  2. JVM利用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指正,如果成功表示竞争到锁,则将锁标志位变成00(表示此对象处于轻量级锁状态),执行同步操作;如果失败则执行步骤3;
  3. 判断当前对象的Mark Word是否指向当前线程的栈帧,如果是则表示当前线程已经持有当前对象的锁,则直接执行同步代码块;否则只能说明该锁对象已经被其他线程抢占了,这时轻量级锁需要膨胀为重量级锁,锁标志位变成10,后面等待的线程将会进入阻塞状态;
    在这里插入图片描述

轻量级锁的释放

  1. 取出在获取轻量级锁保存在Displaced Mark Word中的数据;
  2. 用CAS操作将取出的数据替换当前对象的Mark Word中(上面第一步的时候把原来的Mark Word复制了一份,现在还原回去),如果成功,则说明释放锁成功,否则执行3;
  3. 如果CAS操作替换失败,说明有其他线程尝试获取该锁,则需要在释放锁的同时需要唤醒被挂起的线程。

轻量级锁说明

其实图跟上面的偏向锁差不多,当线程T2举报了之后,偏向锁撤销了,线程T1和线程T2进入了公平竞争模式,由于同步代码块执行的时间很短,所以线程T2进行几次CAS操作的时候成功了1次(如果同步代码块执行时间很长,T2几次CAS操作获取不到锁则晋级为重量级锁),然后T2线程执行代码块,同理线程T1也如此,两个线程交替执行。

三、重量级锁

还是上面的代码,这样使用:

public class Test007 extends Thread {private  Object object = new Object();@Overridepublic void run() {for (int i=0;i<5;i++){synchronized (object) {for(int j=0;j<10000;j++){j=j*1000/1000+1000-1000; //让代码执行时间长点}System.out.println(ClassLayout.parseInstance(object).toPrintable());}}}public static void main(String[] args) throws InterruptedException {new Test007().start();new Test007().start();new Test007().start();new Test007().start();}
}

多个线程同时执行,这时候多个线程同时抢夺锁标记,这时是重量级锁,此时标志位为10
在这里插入图片描述

线程的竞争不会使用自旋,不会消耗cpu资源,适合于同步代码执行比较长的时间

锁的升级图

在这里插入图片描述
偏向锁被举报了,锁升级为轻量级锁或重量级锁。执行时间短为轻量级锁,长为重量级锁,轻量级锁执行时间长了会升级为重量级锁。

总结

锁的优化

  1. 尽量减少Synchronized同步代码块的范围,执行的代码量少了,锁住的同步代码块执行的时间就会减少,当时间减少到一定程度就不使用重量级锁了,只会使用偏向锁或者轻量锁。
  2. 将锁的力度拆分细点,将一个锁拆分为多个锁提高并发度,例如ConcurrentHashMap的实现原理,将一个Map拆分成多个Map,在少量并发情况下让每个线程都持有一个Map,这样就可以少量线程同时执行。
  3. 锁做读写分离,正常读取数据是不用加锁的,只有在删除和修改的时候才会有并发问题,才加锁。

锁的粗化

通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽可能短,但是大某些情况下,一个程序对同一个锁不间断、高频地请求、同步与释放,会消耗掉一定的系统资源,因为锁的讲求、同步与释放本身会带来性能损耗,这样高频的锁请求就反而不利于系统性能的优化了,虽然单次同步操作的时间可能很短。锁粗化就是告诉我们任何事情都有个度,有些情况下我们反而希望把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗

锁的消除

锁削除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行削除。锁削除的主要判定依据来源于逃逸分析的数据支持,如果判断到一段代码中,在堆上的所有数据都不会逃逸出去被其他线程访问到,那就可以把它们当作栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行。

  public static String andString(String s1, String s2, String s3) {return new StringBuffer().append(s1).append(s2).append(s3).toString();}

如果两个线程同时执行该代码就会进行锁的消除,虽然StringBuffer加Synchronized了,但是这里面每个都new了新对象,两个线程的锁标记不是一个,不存在共享竞争关系,虚拟机执行时就会自动去掉Synchronized关键字。

内容来源:蚂蚁课堂