服务器测评网
我们一直在努力

Java同步锁Lock怎么用?与synchronized区别及实战案例详解

Java同步锁Lock的基本概念

在Java多线程编程中,当多个线程同时访问共享资源时,可能会引发数据不一致的问题,为了解决这一问题,Java提供了多种同步机制,其中java.util.concurrent.locks.Lock接口是一种灵活且强大的同步工具,与synchronized关键字相比,Lock提供了更广泛的锁定操作,例如可中断的获取锁、超时获取锁以及公平性选择等,使得线程同步控制更加精细。

20251114182012176311561222951

Lock接口的核心方法

Lock接口定义了几个核心方法,是实现线程同步的基础:

  • lock():获取锁,如果锁被其他线程持有,当前线程会阻塞直到锁被释放。
  • unlock():释放锁,通常在finally块中调用,确保锁一定会被释放,避免死锁。
  • tryLock():尝试获取锁,如果锁可用则立即返回true,否则返回false,不会阻塞线程。
  • tryLock(long time, TimeUnit unit):在指定时间内尝试获取锁,超时后返回false,支持中断响应。
  • lockInterruptibly():可中断地获取锁,如果在获取锁过程中被中断,会抛出InterruptedException

ReentrantLock的使用示例

ReentrantLock是Lock接口最常用的实现类,它支持可重入(同一线程可多次获取锁)和公平性(按请求顺序分配锁)选择,以下是一个基本使用示例:

20251114182013176311561387843

import java.util.concurrent.locks.ReentrantLock;  
public class LockExample {  
    private final ReentrantLock lock = new ReentrantLock();  
    private int count = 0;  
    public void increment() {  
        lock.lock(); // 获取锁  
        try {  
            count++; // 临界区操作  
        } finally {  
            lock.unlock(); // 释放锁  
        }  
    }  
    public static void main(String[] args) {  
        LockExample example = new LockExample();  
        Runnable task = () -> {  
            for (int i = 0; i < 1000; i++) {  
                example.increment();  
            }  
        };  
        Thread t1 = new Thread(task);  
        Thread t2 = new Thread(task);  
        t1.start();  
        t2.start();  
        try {  
            t1.join();  
            t2.join();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("Count: " + example.count); // 输出2000  
    }  
}  

与synchronized的对比

Lock与synchronized的主要区别在于:

  1. 灵活性:Lock支持非阻塞获取锁(tryLock())和可中断获取锁(lockInterruptibly()),而synchronized是阻塞式的。
  2. 公平性:ReentrantLock可设置为公平锁(new ReentrantLock(true)),按线程请求顺序分配锁,而synchronized是非公平的。
  3. 锁绑定:Lock需手动释放锁(unlock()),容易因异常导致死锁,而synchronized在异常时会自动释放锁。
  4. 功能扩展:Lock可与Condition结合实现精确的线程间通信,而synchronized只能配合wait()/notify()使用。

Condition的使用

Lock接口允许创建多个Condition对象,用于实现线程的分组唤醒,以下示例展示了生产者-消费者模型:

20251114182013176311561353484

import java.util.concurrent.locks.Condition;  
import java.util.concurrent.locks.ReentrantLock;  
public class ConditionExample {  
    private final ReentrantLock lock = new ReentrantLock();  
    private final Condition condition = lock.newCondition();  
    private boolean isProduced = false;  
    public void produce() throws InterruptedException {  
        lock.lock();  
        try {  
            while (isProduced) {  
                condition.await(); // 等待消费  
            }  
            System.out.println("Produced");  
            isProduced = true;  
            condition.signal(); // 唤醒消费者  
        } finally {  
            lock.unlock();  
        }  
    }  
    public void consume() throws InterruptedException {  
        lock.lock();  
        try {  
            while (!isProduced) {  
                condition.await(); // 等待生产  
            }  
            System.out.println("Consumed");  
            isProduced = false;  
            condition.signal(); // 唤醒生产者  
        } finally {  
            lock.unlock();  
        }  
    }  
}  

注意事项

  1. 锁的释放:确保在finally块中调用unlock(),避免锁泄漏。
  2. 死锁风险:避免嵌套获取多个锁,或按固定顺序获取锁以防止死锁。
  3. 性能考量:在低竞争场景下,synchronized性能更优;在高竞争或需要高级功能时,Lock更合适。

通过合理使用Lock接口,开发者可以更灵活地控制线程同步,确保多线程环境下的数据安全性和程序稳定性。

赞(0)
未经允许不得转载:好主机测评网 » Java同步锁Lock怎么用?与synchronized区别及实战案例详解