Lock用法和加锁原理

文章介绍了Lock接口的使用方式,特别是ReentrantLock的示例,强调了在finally块中正确释放锁的重要性。同时,文章探讨了Lock相比于synchronized的实现原理,指出Lock主要基于AbstractQueuedSynchronizer(AQS)的双向链表结构来管理线程的同步状态。
摘要由CSDN通过智能技术生成

用法

lock使用语法比较固定,由于不能自动释放锁,同时又避免可能异常倒是锁无法释放,所以需要在finally只中对锁进行释放,使用语法模版如下。
使用模版

lock.lock();
try {
    // 同步代码块
} catch (Exception e) {
} finally {
    lock.unlock();
}

lock用法举例

public class LockTest {
    private static final List<String> list = new ArrayList<>();
    private static final Lock lock  = new ReentrantLock();
    private static CountDownLatch latch;
    private static Integer counter = 1;
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        LockTest lockTest = new LockTest();
        latch = new CountDownLatch(2);
        executor.execute(() -> lockTest.addName());
        executor.execute(() -> lockTest.addName());
        try {
            latch.await();
        } catch (InterruptedException e) {
        }
        System.out.println(list);
        executor.shutdownNow();
    }
    
    public void addName() {
        lock.lock();
        try {
            /****************************业务代码(开始)****************/
            System.out.println(Thread.currentThread().getName()+ "lock");
            for(; counter < 10 ; counter++) {
                list.add(String.format("%d_%s", counter, Thread.currentThread().getName()));
            }
            System.out.println(Thread.currentThread().getName()+ "unlock");
            /***************************业务代码(结束)*****************/
        } catch (Exception e) {
        } finally {
            lock.unlock();
            latch.countDown();
        }
    }
}

注意点
note: 1)加锁语句lock.lock();后面必须try语句之前;2)lock.unlock()语句必须是finally中第一条语句。不禁问why,只所以这样,在加锁到释放锁之前所有发生的异常都是在try …catch语句中发生的,这两个部分可能发生的异常不影响finally执行。如果在加锁lock.lock()和try之间还可以插入语句,那中间就有可能发生异常,这中间的发生的异常因为没有被捕捉catch,导致程序无法继续执行,finally语句也无法执行。同理,如果lock.unlock()不是finally的第一条语句,那么finally中依然可能发生异常,此时发生的异常也由于没有对应的处理而导致程序无法执行,无法执行解锁语句。

实现原理

synchronized同步机制是依赖JVM实现,Lock主要依赖抽象队列同步器(AbstractQueuedSynchronier,AQS)实现的,AQS是一个双向链表,使用一个整型变量state表示对共享区域的加锁状态,对于每个尝试访问临界区的线程都会被封装成双向链表中的一个节点,主要通过tryacquired获取临界区访问权,通过tryrelease释放临界区访问权。

Bibliography

1 深入浅出 sychronized 与 Lock 的实现原理
2 锁的底层实现原理

### Java 中 `Lock` 接口 `synchronized` 关键字的工作原理 #### 工作原理 `synchronized` 是一种内置的关键字,其工作依赖于 JVM 的监视器锁(Monitor Lock)。当一个线程进入由 `synchronized` 保护的方法或者代码块时,该线程必须获取到对应的对象锁才能继续执行。如果此时其他线程已经持有此对象上的锁,则当前尝试获取的对象会被阻塞直到前一线程释放锁为止[^1]。 对于 `Lock` 接口而言,这是显式的锁定机制的一部分,提供了更灵活丰富的功能集。开发者需要手动调用相应的方法去获得以及释放锁,比如使用 `lock()` 方法加锁并配合 try-finally 结构确保最终能通过 `unlock()` 来解锁。这种灵活性允许程序员更好地控制何时何处应用同步逻辑[^5]。 ```java // 使用 synchronized 实现同步 public class SyncExample { private final Object lockObject = new Object(); public void syncMethod() { synchronized (lockObject) { // 同步操作... } } } // 使用 Lock 实现同步 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockExample { private final Lock lock = new ReentrantLock(); public void lockedMethod() { lock.lock(); try { // 同步操作... } finally { lock.unlock(); // 确保总是会解锁 } } } ``` #### 主要区别 - **语法差异**: `synchronized` 是作为语言特性存在的关键字,而 `Lock` 则是一个 API 提供者定义的一组类接口。 - **性能表现**: 尽管早期版本中 `synchronized` 被认为效率低下,但从 JDK 1.6 开始经过大量优化之后,在大多数情况下两者之间的差距变得很小甚至可以忽略不计。不过某些特定场景下 `Lock` 可能提供更好的性能选项[^2]。 - **功能性对比**: - `Lock` 支持条件变量、公平策略设置等功能; - `synchronized` 自动管理锁的状态变化过程,无需担心忘记解锁等问题发生; - 当遇到异常情况时,`synchronized` 会在退出作用域的同时自动释放持有的锁,但 `Lock` 需要在 catch 或 finally 块里明确处理这个问题[^3]。 - **适用范围**: - 对于简单的互斥需求来说,`synchronized` 更简洁易读; - 如果项目中有复杂多变的需求或是追求更高的定制化程度,则应考虑采用 `Lock`[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值