Java锁 从乐观锁和灰心锁开始讲 面试复盘

打印 上一主题 下一主题

主题 859|帖子 859|积分 2577

目录

面试复盘
Java 中的锁 大全
灰心锁
专业表明
自我理解
乐观锁
专业表明
自我理解
灰心锁的调用
乐观锁的调用
synchronized和 ReentrantLock的区别
雷同点
区别
具体对比
总结


面试复盘



Java 中的锁 大全



灰心锁

专业表明

得当写操纵多的场景
先加锁可以保证写操纵时数据精确
显式的锁定之后再操纵同步资源
自我理解

灰心锁以为自己使用数据的时间一定有其他线程来修改数据
因此在获取数据的时间会选择先加锁
确保数据不会被别的线程修改
synchronized 和 Lock 的实现类都是灰心锁

乐观锁

专业表明

得当读操纵多的场景
不加锁的特点能够使其读操纵的性能大幅提拔
乐观锁则能直接去操纵同步资源
是一种无锁算法
自我理解

乐观锁以为自己在操纵数据的时间不会有别的线程修改数据,所以不会加锁,所以他只会在自己操纵数据的时间检查是否有其他线程修改更新的这个数据。
如果乐观锁去操纵数据,这个数据没有更新的话。当火线程会直接将修改乐成的数据写入,如果数据已经被其他线程更新了。要通过不同的实现方式举行不同操纵。乐观锁在Java中是通过使用无锁编程来实现的,常用的是CAS算法。
Java原子类中的递增操纵就是用CAS 自旋完成的

灰心锁的调用



  1. import java.util.concurrent.locks.ReentrantLock;
  2. public class OptiPessLockDemo {
  3.     // 悲观锁的调用方式
  4.     public  synchronized void m1(){
  5.         // 加锁后的业务逻辑...
  6.     }
  7.     // 保证多个线程使用的是同一个lock对象的前提下
  8.     ReentrantLock lock=new ReentrantLock();
  9.     public void m2(){
  10.         lock.lock();
  11.         try {
  12.             // 操作同步资源
  13.         }finally {
  14.             lock.unlock();
  15.         }
  16.     }
  17.     // 两个都是悲观锁
  18. }
复制代码
假设在任何时间都可能发生冲突,因此,线程必须显式地获取锁以确保数据一致性和线程安全,直到它实行完毕并释放锁。
对于 synchronized,在方法级别加锁时,锁是针对该对象的,保证同一时刻只有一个线程能够实行该方法。因此,当一个线程在实行 m1() 方法时,其他线程不能同时实行同一个对象上的 m1() 方法。


  • synchronized 关键字的锁粒度是方法级别的,锁住的是整个方法。在方法实行期间,其他线程不能进入这个方法。
  • synchronized 实现的是隐式加锁息争锁,不必要显式地调用 lock() 和 unlock()。
  • ReentrantLock 是 Java 中的一个显式锁,它提供了比 synchronized 更细粒度的锁控制和更多功能。
  • ReentrantLock 通过 lock.lock() 显式地加锁,调用 lock.unlock() 来释放锁。通常,这两个操纵会放在 try...finally 代码块中,以确保即使出现非常,锁也能得到释放。
乐观锁的调用



  1. import java.util.concurrent.atomic.AtomicInteger;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class OptiPessLockDemo2 {
  4.     // 乐观锁的调用方式
  5.     private AtomicInteger atomicInteger = new AtomicInteger();
  6.     atomicInteger.incrementAndGet();
  7.     public static void main(String[] args) {
  8.         int oldValue = atomicInteger.get();
  9.         int newValue = oldValue + 1;
  10.         // 如果值没有被修改,原子性操作成功
  11.         return atomicInteger.compareAndSet(oldValue, newValue);
  12.     }
  13. }
复制代码
这种方式在判定和更新之间,确保了只有一个线程能够乐成更新值,其他线程则会重试或失败,从而保证了乐观锁的行为。
总之,乐观锁的核心是盼望在操纵时不加锁,直到最后验证冲突发生与否。如果有冲突,则可以通过重试、回滚等方式处置处罚。

synchronized和 ReentrantLock的区别

synchronized 和 ReentrantLock 都用于实现 灰心锁(Pessimistic Locking),即在多线程环境中对共享资源举行加锁,以保证线程安全。尽管两者实现的目标雷同,它们在使用方式、机动性、性能等方面有一些不同。下面我们来具体分析它们的 雷同点区别
雷同点


  • 线程安全




    • 两者都能确保在多线程环境下,访问共享资源时,只有一个线程能够持有锁,防止多个线程同时修改共享数据,制止数据不一致。


  • 互斥性




    • 两者都保证了同一时刻只有一个线程能够实行被掩护的代码块或方法。其他线程必须等候当火线程释放锁后才能实行。


  • 支持重入性




    • synchronized 和 ReentrantLock 都是可重入的(Reentrant),即同一线程可以多次获取同一个锁,而不会发生死锁。


  • 作用范围




    • 两者都可以应用于同步方法或同步代码块,掩护共享资源。


区别

特性
synchronized
ReentrantLock
实现方式
隐式加锁,Java 编译器在编译时自动处置处罚。
显式加锁,必要手动调用 lock()
和 unlock()

锁粒度
锁定整个方法或代码块,无法精确控制。
可以精确控制锁定的范围,答应更机动的锁定操纵。
性能
相对较低,特别是在高并发环境下,由于 JVM 的锁优化不足,可能导致性能瓶颈。
在高并发时,ReentrantLock
性能优于 synchronized
,尤其在锁竞争激烈时。
中断支持
不支持中断,线程获取锁时无法响应中断。
支持中断,可以使用 lockInterruptibly()
来在等候锁时响应中断。
公平性
非公平锁,线程不一定按照哀求的顺序获取锁。
可以选择公平锁或非公平锁。使用构造函数 new ReentrantLock(true)
来创建公平锁。
死锁制止
必要小心死锁问题,synchronized
无法制止死锁。
通过 ReentrantLock
提供的 tryLock()
方法和超时机制可以更机动地制止死锁。
锁释放机制
锁由 JVM 自动管理,方法实行完后自动释放。
必须手动调用 unlock()
释放锁,通常与 try...finally
语句配合使用。
可重入性
支持,可同一线程多次获取同一锁。
支持,可同一线程多次获取同一锁。
性能监控
无法直接获取锁的状态。
可以通过 getHoldCount()
获取当火线程持有锁的次数,举行监控。
锁升级
不支持锁的升级(无法从轻量级锁升级为重量级锁)。
可以通过锁的竞争情况动态升级为不同的锁类型(如偏向锁、轻量锁、重量锁)。

具体对比


  • 锁的获取与释放




    • synchronized 是 隐式加锁,锁的获取和释放是自动完成的。线程在实行被 synchronized 修饰的代码时,会自动获取该对象的锁,方法或代码块实行完后自动释放锁。
    • ReentrantLock 是 显式加锁,必须手动调用 lock() 获取锁,必须手动调用 unlock() 释放锁。通常会配合 try...finally 语句使用,以保证在实行完业务逻辑后无论是否发生非常都能够释放锁。


  • 中断响应




    • synchronized 在获取锁时无法响应中断。如果线程在等候锁的过程中被中断,它会继续等候,直到获取到锁。
    • ReentrantLock 提供了 lockInterruptibly() 方法,它答应在等候锁的过程中响应中断。线程在等候锁时,如果被中断,能够及时退出等候。


  • 公平性




    • synchronized 是 非公平锁,即任何线程在哀求锁时,获取锁的顺序并不一定按照哀求的顺序。可能先被调用的线程后获得锁,后调用的线程反而先获取到锁。
    • ReentrantLock 可以通过构造函数指定是否为 公平锁。如果设置为公平锁,锁会按照哀求顺序分配给线程,先哀求的线程会先获得锁。公平锁相对会增长一些性能开销,因此通常默认黑白公平锁。


  • 性能




    • 在低并发环境下,synchronized 的性能和 ReentrantLock 差不多,但在高并发环境下,ReentrantLock 会由于其机动性和优化而体现得更好。
    • synchronized 锁的实现较为简单,但在高并发下可能存在 锁竞争 的问题,导致性能瓶颈。
    • ReentrantLock 在 锁竞争 情况下提供了更多的优化方式,如通过自旋锁、CAS 等机制来减少线程的阻塞,从而提高性能。


  • 死锁制止




    • synchronized 并没有提供直接的 API 来制止死锁。必要开发者自己通过代码计划来制止死锁问题。
    • ReentrantLock 提供了 tryLock() 方法,可以尝试获取锁,如果获取失败,可以选择放弃或重试。这种机制可以帮助开发者制止死锁或减少锁的等候时间。


  • 锁的升级和监控




    • synchronized 锁的状态无法直接监控,只能通过 JVM 的内部调试工具举行查看。
    • ReentrantLock 提供了诸如 getHoldCount() 等方法来获取当火线程持有锁的次数,此外,还可以通过 getQueueLength() 来获取等候锁的线程数量,便于性能监控和分析。


总结



  • synchronized:适用于简单的同步需求,易于使用,代码简洁,但不敷机动,性能在高并发场景下可能有所降落。
  • ReentrantLock:功能更强盛,提供了更多机动性,如公平锁、中断响应、尝试加锁等,适用于复杂的多线程应用。但必要手动管理锁的获取与释放,使用上轻微复杂一些。
如果你的应用需求比力简单,且对性能要求不高,使用 synchronized 就足够了。如果你必要更多的控制、机动性和对高并发场景的优化,ReentrantLock 会是更好的选择。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曂沅仴駦

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表