JAVA中的锁

JAVA中的锁

1、Lock 与 Synchronized 区别

  •   实现方式:

       Synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定

       Lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

  •   锁的获取:

       例如:线程A和B都要获取对象O的锁,假设A获取了对象O锁,B将等待A释放对O的锁定,
       如果使用 Synchronized ,如果A不释放,B将一直等下去,不能被中断
       如果使用 Lock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

              Lock获取锁定与三种方式:
              a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
              b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
              c) tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
              d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

  •   性能:

         在资源竞争不是很激烈的情况下,Synchronized的性能要优于Lock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是Lock的性能能维持常态;

2、锁的分类

                  锁的分类

### Java 中的机制概述 Java 提供了多种机制以支持多线程环境下的同步操作。这些机制主要包括内置(synchronized 关键字)、显示(ReentrantLock 类)以及一些高级特性如读写(ReadWriteLock)。以下是关于它们的具体介绍: --- ### 内置(Intrinsic Lock) Java 的 synchronized 关键字提供了一种简单的方式来进行线程间的同步。每个对象都有一个与之关联的监视器(monitor),当一个线程进入由 synchronized 修饰的方法或代码块时,它会自动获得该对象的监视器。 ```java public class Counter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } } ``` 上述代码展示了如何使用 synchronized 方法来确保多个线程不会同时修改 `count` 变量[^1]。 --- ### 显示(Explicit Lock) 相比于内置,显示提供了更大的灵活性。`ReentrantLock` 是最常用的显示实现之一,它可以手动控制的获取和释放,并且支持可中断等待、超时尝试等功能。 #### 使用示例 ```java import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Worker { private final Lock lock = new ReentrantLock(); private int value = 0; public void updateValue(int newValue) throws InterruptedException { boolean isLocked = false; try { isLocked = lock.tryLock(1000, java.util.concurrent.TimeUnit.MILLISECONDS); if (isLocked) { value = newValue; } else { System.out.println("Failed to acquire the lock."); } } finally { if (isLocked) { lock.unlock(); } } } public int getValue() { lock.lock(); try { return value; } finally { lock.unlock(); } } } ``` 此代码片段演示了如何利用 `tryLock()` 方法在一定时间内尝试获取,如果无法获取,则可以选择其他逻辑路径[^1]。 --- ### 偏向(Biased Locking) 偏向是一种优化技术,旨在减少无竞争情况下的操作开销。它的基本思想是在第一次访问某个时将其绑定到当前线程,后续再次访问时不需额外验证即可直接执行。 然而,一旦检测到有其他线程尝试获取同一个,JVM 将撤销偏向并升级为轻量级甚至重量级。这一过程发生在全局安全点处,因为需要暂停所有活动线程以重新评估的状态[^3]。 --- ### 轻量级(Lightweight Locking) 轻量级通过自旋的方式来避免线程切换带来的性能损失。具体来说,当第一个线程请求时,它会在栈帧中保存指向记录的信息;第二个线程到达时发现已被占用,则启动循环不断检查是否可用。只有当自旋次数达到上限仍未能成功时,才会退化成重量级[^4]。 --- ### 解决 ABA 问题 在高并发场景下可能出现一种称为 ABA 的现象:某一变量先后经历了两次相同值的变化,但其间可能发生了不可忽略的动作。为此引入了带版本号的原子引用类 `AtomicStampedReference` 来加以防范[^5]。 ```java import java.util.concurrent.atomic.AtomicStampedReference; public class ABADemo { static AtomicStampedReference<Integer> atomicRef = new AtomicStampedReference<>(100, 0); public static void main(String[] args) { new Thread(() -> { int stamp = atomicRef.getStamp(); try {Thread.sleep(1);} catch (Exception ignored){} atomicRef.compareAndSet(100, 101, stamp, stamp + 1); atomicRef.compareAndSet(101, 100, stamp + 1, stamp + 2); }).start(); new Thread(() -> { int stamp = atomicRef.getStamp(); try {Thread.sleep(2);} catch (Exception ignored){} boolean result = atomicRef.compareAndSet(100, 101, stamp, stamp + 1); System.out.println(result ? "Success" : "Failure"); }).start(); } } ``` 以上例子表明即使目标数值看似未变,但由于附加了时间戳标记,依然能有效区分实际发生的改动序列[^5]。 --- ### 高效实践建议 - 对于短期定需求优先考虑内置; - 如果涉及复杂业务流程推荐选用显式以便更好地掌控资源分配状况; - 注意合理配置粒度大小以免造成不必要的争用瓶颈。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值