Java锁机制以及原理

本文详细探讨了Java中的同步机制,包括synchronized关键字、volatile变量、显示锁和原子变量。深入分析了线程安全的概念,指出无状态和不可变对象的线程安全性。还介绍了锁的内置实现,如监视器锁和volatile的应用,以及避免死锁的策略。此外,文章讨论了Java并发机制的底层实现,特别是volatile和synchronized的工作原理,以及线程安全的集合类如CopyOnWriteArrayList。

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

    1.同步方式

             Java中主要同步机制是关键字 synchronized (独占锁),volatile类型的变量、显示锁(Lock)、原子变量(volatile与CAS方式);

             synchronized 加锁的含义不仅仅局限于互斥行为,还包括内存可见性,为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。同步代码块与同步方法是一样的,都是当前对象锁。

    2. 线程安全

             当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为;

            无状态对象一定是线程安全的;

            不可变对象一定是线程安全的(final),final域能确保初始化过程的安全性,从而可以不受限制地访问不可变对象,并在共享这些对象时无须同步;

            要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量;       

    3.锁

           内置锁(监视器锁Monitor Lock) synchrnized 同步代码块;线程在进入同步代码块之前会自动获得锁,并且在退出同步代码块时自动释放锁。他是一种互斥锁,只有一个线程能持有这种锁。当线程A持有锁时,线程B只能等待或者阻塞,直到线程B释放。 内置锁是可重入的,“重入”意味着获取锁的操作的粒度是“线程”,而不是“调用”。获取自身锁的同时会获取父类的锁;

        volatile变量,只能确保可见性,不能确保原子性(不会将该变量上的操作与其他内存操作一起重排序 禁止重排序优化),不能保证互斥性;

         注意:1)、对于可能被多个线程同时访问的可变状态变量,在访问它时都需要持有同一个锁,在这个情况下,我们称状态变量是由这个锁保护的。 2)、加锁的含义不仅仅局限于互斥行为,还包括内存的可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或写操作的线程都必须在同一个锁上同步。

  4.死锁

          造成死锁的原因:两个线程相互等待对方释放锁。

          避免死锁的方法:

              1)、避免一个线程同时获取多个锁;

              2)、避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源;

              3)、尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制;

              4)、对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况; 

 

  5.Java并发机制的底层实现原理

       Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令。

 5.1 volatile应用

        Lock操作会引发两件事情: 1) 将当前处理器缓存行的数据写回到系统内存;   2) 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效;

        为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存(L1、L2)后再进行操作,但操作完不知道何时会写到内存。如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行操作就会有问题,所以,在多处理器下,为了保证各个处理器缓存是一致的,就会实现缓存一致性协议,每个处理器通过吃嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。

 

5.2 synchronized应用

重量级锁,但在1.6后对synchronized进行了各种优化之后,为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁。

实现同步的基础:Java中每一个对象都可以作为锁。具体表现为以下3种形式:

          1、 对于普通同步方法,锁是当前实例对象;

          2、对于静态同步方法,锁是当前类的Class对象;

          3、 对于同步方法块,锁是synchonized括号里配置的对象;

实现原理:JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的。

Java对象头: synchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用3个字节宽存储对象头,如果对象是非数组类型,则用2字宽存储对象头。1字节宽=4字节 即32位 bit。 对象头默认存储对象的HashCode、分代年龄和锁标记位。

锁的四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态;锁可以升级但不能降级,目标为了提高获得锁和释放锁的效率。

       

 

  6.安全的集合类

           CopyOnWriteArrayList(并发容器)实现原理:读写分离的思想;add方法会加锁,ReetrantLock锁,只能由一个线程去添加,这个线程是可重入的。对复制后产生的新数组进行操作

          Vector\HashTable\CopyOnWriteArrayList\CopyOnWriteSet\BlockingQueue\ConcurrentLinkedQueue\Collections.synchronizedList

 

 

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值