并发安全笔记1——synchronized

本文深入探讨了Java中的Synchronized、Atomic类和Lock在并发控制中的原子性、可见性和有序性。介绍了锁的实现原理,如对象头的MarkWord、偏向锁和轻量级锁等,并分析了不同锁状态的优缺点及适用场景。同时,提到了锁粗化和锁消除等优化技术,以提升并发性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 原子性:Synchronized、AtomicXXX、Lock
  • 可见性:Synchronized、volatile
  • 有序性:Synchronized、volatile

Synchronized同步锁:排他锁、互斥锁同一时刻只允许一个线程执行。

public synchronized void methodA(){
        
    }

    public  void methodB(){
        synchronized (this){
            
        }
    }

加锁要注意范围,因为加锁一定会带来性能开销

synchronized 作用范围

  • 修饰实例方法  跨对象不互斥
  • 修饰静态方法 无论是否跨对象都互斥
  • 修饰代码块   ()可以存贮任何一个对象作为锁 

影响锁的作用范围,就是对象本身的生命周期。

抢占锁的本质是什么?互斥

如何实现互斥?

  • 共享资源
  • 可以是一个标记,0无锁 1有锁
  • 锁存放在对象头
<dependency>
     <groupId>org.openjdk.jol</groupId>
     <artifactId>jol-core</artifactId>
     <version>0.9</version>
</dependency>
/**
 * @author:hubinbin
 * @date:2021/9/3
 */
public class ClassLayoutDemo {
    public static void main(String[] args) {
        ClassLayoutDemo classLayoutDemo=new ClassLayoutDemo();
        System.out.println(ClassLayout.parseInstance(classLayoutDemo).toPrintable());
    }
}


 com.icode.demo.lock.ClassLayoutDemo 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)

//Klass pointer 类型指针描述对象的具体类
      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

  1. Java中对象在内存中的布局
  • 对象头:
  1. MarkWord:用于存储对象自身运行时数据。如果是数组对象占用3个字宽,非数字占用2个字宽。对象锁就是放在MarkWord中。
  2. Klass Point(类型指针):是对象指向它的类元数据的指针,虚拟机通过这个歌指针确定这个对象的类信息。
  • 实例变量
  • 填充字符

偏向锁

     2.  偏向锁升级

   3.  偏向锁取消

偏向锁是默认开启的,而且开始时间一般是比应用程序启动慢几秒,如果不想有这个延迟,那么可以使用-XX:BiasedLockingStartUpDelay=0;

如果不想要偏向锁,那么可以通过-XX:-UseBiasedLocking = false来设置;

 轻量级锁

为什么要引入轻量级锁?

轻量级锁考虑的是竞争锁对象的线程不多,而且线程持有锁的时间也不长的情景。因为阻塞线程需要CPU从用户态转到内核态,代价较大,如果刚刚阻塞不久这个锁就被释放了,那这个代价就有点得不偿失了,因此这个时候就干脆不阻塞这个线程,让它自旋这等待锁释放

轻量锁升级

 为了避免不必要的自旋,锁一旦升级(偏量锁——>轻量级锁 / 轻量级锁 ——>重量级锁),就不会主动降级。但偏量锁可以降级到无锁

锁状态优点缺点适用场景
偏向锁

加锁解锁无需额外的消耗,

和非同步方法相差纳秒级别

竞争多时会带来额外锁撤销开销基本没有线程竞争的同步场景
轻量级锁竞争的线程不会阻塞,使用自旋锁,提高程序响应速度如果一直不能获取锁,长时间自旋让CPU空转适用于少量线程竞争,且线程持有锁时间不长,追求响应速度的场景
重量级锁线程竞争不使用自旋,不会导致CPU空转消耗资源线程阻塞,响应时间长很多线程竞争锁,且锁被长时间占有,追求吞吐量的场景

锁粗化

按理来说,同步块的作用范围应该尽可能小,仅在共享数据的实际作用域中才进行同步,这样做的目的是为了使需要同步的操作数量尽可能缩小,缩短阻塞时间,如果存在锁竞争,那么等待锁的线程也能尽快拿到锁。 
但是加锁解锁也需要消耗资源,如果存在一系列的连续加锁解锁操作,可能会导致不必要的性能损耗。 
锁粗化就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁,避免频繁的加锁解锁操作。

锁消除

Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,经过逃逸分析,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值