Java中Lock和Condition用法详解

在Java中,Lock接口是java.util.concurrent.locks包中的一部分,它提供了比synchronized关键字更灵活的锁机制。Lock接口允许更细粒度的锁控制,支持尝试获取锁、超时获取锁、可中断获取锁等操作。Lock 用于控制对共享资源的访问,而 Condition 则用于线程间的协调。

1. Lock接口的主要方法

Lock接口定义了以下主要方法:

  • void lock(): 获取锁。如果锁不可用,当前线程将被阻塞,直到锁可用。
  • void lockInterruptibly(): 获取锁,但允许在等待锁的过程中被中断。
  • boolean tryLock(): 尝试获取锁,如果锁可用则立即返回true,否则返回false
  • boolean tryLock(long time, TimeUnit unit): 尝试在指定的时间内获取锁,如果成功则返回true,否则返回false
  • void unlock(): 释放锁。
  • Condition newCondition(): 返回一个与当前锁关联的Condition对象,用于线程间的协调。

2. ReentrantLock

ReentrantLockLock接口的一个实现类,它是一个可重入锁,意味着同一个线程可以多次获取同一个锁。

2.1 基本用法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final Lock lock = new ReentrantLock();

    public void performTask() {
        lock.lock();  // 获取锁
        try {
            // 临界区代码
            System.out.println(Thread.currentThread().getName() + " is performing the task.");
        } finally {
            lock.unlock();  // 释放锁
        }
    }

    public static void main(String[] args) {
        LockExample example = new LockExample();

        Runnable task = example::performTask;

        Thread thread1 = new Thread(task, "Thread-1");
        Thread thread2 = new Thread(task, "Thread-2");

        thread1.start();
        thread2.start();
    }
}

在上面的例子中,performTask方法使用lock.lock()获取锁,并在finally块中释放锁,以确保锁总是被释放。

2.2 尝试获取锁
public void performTaskWithTryLock() {
    if (lock.tryLock()) {
        try {
            // 临界区代码
            System.out.println(Thread.currentThread().getName() + " is performing the task.");
        } finally {
            lock.unlock();
        }
    } else {
        System.out.println(Thread.currentThread().getName() + " could not acquire the lock.");
    }
}

tryLock()方法尝试获取锁,如果锁不可用,则立即返回false,不会阻塞线程。

2.3 超时获取锁
public void performTaskWithTimeout() {
    try {
        if (lock.tryLock(1, TimeUnit.SECONDS)) {
            try {
                // 临界区代码
                System.out.println(Thread.currentThread().getName() + " is performing the task.");
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " could not acquire the lock within the timeout.");
        }
    } catch (InterruptedException e) {
        System.out.println(Thread.currentThread().getName() + " was interrupted while waiting for the lock.");
    }
}

tryLock(long time, TimeUnit unit)方法尝试在指定的时间内获取锁,如果在指定时间内未能获取锁,则返回false

2.4 可中断获取锁
public void performTaskInterruptibly() {
    try {
        lock.lockInterruptibly();
        try {
            // 临界区代码
            System.out.println(Thread.currentThread().getName() + " is performing the task.");
        } finally {
            lock.unlock();
        }
    } catch (InterruptedException e) {
        System.out.println(Thread.currentThread().getName() + " was interrupted while waiting for the lock.");
    }
}

lockInterruptibly()方法允许在等待锁的过程中被中断。

2.5 锁的公平性

ReentrantLock 支持公平锁(按等待顺序分配锁),默认为非公平锁。

Lock fairLock = new ReentrantLock(true); // 创建公平锁

3. Condition 接口

Condition 接口用于线程间的协调。它类似于 Objectwait()notify() 方法,但提供了更灵活的功能。

3.1 常用方法
方法描述
void await()等待,直到被唤醒或中断
void awaitUninterruptibly()不可中断等待
long awaitNanos(long nanos)等待指定纳秒
boolean await(long time, TimeUnit unit)超时等待
boolean awaitUntil(Date deadline)等待到指定时间
void signal()唤醒一个等待线程
void signalAll()唤醒所有等待线程
3.2 使用示例
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean ready = false;

    public void waitForReady() throws InterruptedException {
        lock.lock();
        try {
            while (!ready) {
                condition.await();  // 等待条件满足
            }
            System.out.println("Ready!");
        } finally {
            lock.unlock();
        }
    }

    public void setReady() {
        lock.lock();
        try {
            ready = true;
            condition.signalAll();  // 通知所有等待的线程
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ConditionExample example = new ConditionExample();

        Thread waiter = new Thread(() -> {
            try {
                example.waitForReady();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread setter = new Thread(() -> {
            try {
                Thread.sleep(2000);  // 模拟一些耗时操作
                example.setReady();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        waiter.start();
        setter.start();

        try {
            waiter.join();
            setter.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
3.3 Condition 的高级用法
超时等待
public boolean awaitWithTimeout() throws InterruptedException {
    lock.lock();
    try {
        return notEmpty.await(1, TimeUnit.SECONDS); // 最多等待1秒
    } finally {
        lock.unlock();
    }
}
不可中断等待
public void awaitUninterruptiblyExample() {
    lock.lock();
    try {
        notEmpty.awaitUninterruptibly(); // 即使被中断也不退出
    } finally {
        lock.unlock();
    }
}
多条件队列
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition(); // 条件队列A
Condition conditionB = lock.newCondition(); // 条件队列B

3.4 Condition vs wait()/notify()
特性Conditionwait()/notify()
锁机制必须与 Lock 配合必须与 synchronized 配合
多条件队列支持✅ 一个锁可关联多个 Condition❌ 只能有一个等待队列
超时等待await(long time, TimeUnit unit)
不可中断等待awaitUninterruptibly()
灵活性

4. Lock 和 Condition 的结合使用

LockCondition 通常结合使用,以实现更复杂的线程同步机制。例如,生产者-消费者问题可以使用 LockCondition 来实现。

4.1 生产者-消费者示例
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumerExample {
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private final int[] buffer;
    private int count = 0;
    private int putIndex = 0;
    private int takeIndex = 0;

    public ProducerConsumerExample(int size) {
        buffer = new int[size];
    }

    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            while (count == buffer.length) {
                notFull.await();  // 等待缓冲区不满
            }
            buffer[putIndex] = value;
            putIndex = (putIndex + 1) % buffer.length;
            count++;
            notEmpty.signal();  // 通知消费者可以消费
        } finally {
            lock.unlock();
        }
    }

    public int consume() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await();  // 等待缓冲区不空
            }
            int value = buffer[takeIndex];
            takeIndex = (takeIndex + 1) % buffer.length;
            count--;
            notFull.signal();  // 通知生产者可以生产
            return value;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ProducerConsumerExample example = new ProducerConsumerExample(10);

        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    example.produce(i);
                    System.out.println("Produced: " + i);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    int value = example.consume();
                    System.out.println("Consumed: " + value);
                    Thread.sleep(200);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        producer.start();
        consumer.start();

        try {
            producer.join();
            consumer.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

5. 总结

  • Lock 提供了比 synchronized 更灵活的锁机制,支持尝试获取锁、超时获取锁、可中断获取锁等操作。
  • ReentrantLockLock接口的一个实现类,支持可重入锁。
  • tryLock()tryLock(long time, TimeUnit unit)方法允许尝试获取锁,避免线程阻塞。
  • Condition 接口用于线程间的协调,类似于wait()notify(),提供了比 wait()notify() 更灵活的线程协调机制,支持多个条件队列。
  • LockCondition 通常结合使用,以实现复杂的线程同步机制,如生产者-消费者问题。
  1. Lock 的优势

    • 提供更灵活的锁控制(可重入、可中断、超时、公平锁)。
    • 需手动释放锁,确保在 finally 块中调用 unlock()
  2. Condition 的核心价值

    • 替代 wait()/notify(),支持多条件队列和精细唤醒。
    • 适用于生产者-消费者、线程池任务调度等场景。
  3. 使用建议

    • 优先使用 LockCondition 替代 synchronized,以提升灵活性和性能。
    • 在复杂多线程场景中,通过多条件队列减少不必要的唤醒。

通过使用LockCondition,你可以更灵活地控制多线程的同步和协调。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值