并发编程原理与实战(十七)Lock接口API详解与优势举例

上一篇学习了为什么需要Lock接口,相比synchronized关键字Lock接口具有更灵活的锁控制能力‌,本文来进一步学习Lock接口的相关方法。

Lock核心API详解

Lock接口是JDK1.5之后引入的并发工具,声明的方法并不多,只有6个,是对锁的主要特性(上锁和解锁)的核心抽象。

1、lock()

在这里插入图片描述

/**
 * Acquires the lock.
 *
 * <p>If the lock is not available then the current thread becomes
 * disabled for thread scheduling purposes and lies dormant until the
 * lock has been acquired.
 *
 * <p><b>Implementation Considerations</b>
 *
 * <p>A {@code Lock} implementation may be able to detect erroneous use
 * of the lock, such as an invocation that would cause deadlock, and
 * may throw an (unchecked) exception in such circumstances.  The
 * circumstances and the exception type must be documented by that
 * {@code Lock} implementation.
 */
void lock();

线程调用该方法将获取锁,若锁不可用,则出于线程调度目的,当前线程将被禁止执行并进入休眠状态,直至成功获取锁。实现注意事项:Lock接口的具体实现类可检测锁的错误使用(如可能导致死锁的调用),并在该场景下抛出(非受检)异常。具体场景及异常类型必须由实现类说明。

2、lockInterruptibly()

/**
 * Acquires the lock unless the current thread is
 * {@linkplain Thread#interrupt interrupted}.
 ...
 * @throws InterruptedException if the current thread is
 *         interrupted while acquiring the lock (and interruption
 *         of lock acquisition is supported)
 */
void lockInterruptibly() throws InterruptedException;

调用该方法将获取锁,和lock()方法最大的区别是支持线程中断,在阻塞期间响应中断,避免死等

3、tryLock()

/**
 * Acquires the lock only if it is free at the time of invocation.
 *
 * <p>Acquires the lock if it is available and returns immediately
 * with the value {@code true}.
 * If the lock is not available then this method will return
 * immediately with the value {@code false}.
 *
 * <p>A typical usage idiom for this method would be:
 * <pre> {@code
 * Lock lock = ...;
 * if (lock.tryLock()) {
 *   try {
 *     // manipulate protected state
 *   } finally {
 *     lock.unlock();
 *   }
 * } else {
 *   // perform alternative actions
 * }}</pre>
 *
 * This usage ensures that the lock is unlocked if it was acquired, and
 * doesn't try to unlock if the lock was not acquired.
 *
 * @return {@code true} if the lock was acquired and
 *         {@code false} otherwise
 */
boolean tryLock();

仅当调用时锁处于空闲状态才获取锁。若锁可用则立即获取,返回true;若锁不可用则立即返回false。典型使用模式:

Lock lock = ...;
if (lock.tryLock()) {
    try {
      //操作受保护资源
    } finally {
      lock.unlock();
    }
} else {
	//执行备选逻辑
}

此模式确保:若获取锁则必释放,未获取锁则不尝试释放。返回true 表示成功获取锁,否则返回false。可尝试获取锁,这也是和synchronized关键字的区别之一,synchronized是不可尝试获取锁的。和lock() 方法最大的区别是立即返回获锁结果,适用于快速失败场景。

4、tryLock(long time, TimeUnit unit)

/**
 * Acquires the lock if it is free within the given waiting time and the
 * current thread has not been {@linkplain Thread#interrupt interrupted}.
 *
 * <p>If the lock is available this method returns immediately
 * with the value {@code true}.
 * If the lock is not available then
 * the current thread becomes disabled for thread scheduling
 * purposes and lies dormant until one of three things happens:
 * <ul>
 * <li>The lock is acquired by the current thread; or
 * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
 * current thread, and interruption of lock acquisition is supported; or
 * <li>The specified waiting time elapses
 * </ul>
 *
 * <p>If the lock is acquired then the value {@code true} is returned.
 *
 * <p>If the current thread:
 * <ul>
 * <li>has its interrupted status set on entry to this method; or
 * <li>is {@linkplain Thread#interrupt interrupted} while acquiring
 * the lock, and interruption of lock acquisition is supported,
 * </ul>
 * then {@link InterruptedException} is thrown and the current thread's
 * interrupted status is cleared.
 *
 * <p>If the specified waiting time elapses then the value {@code false}
 * is returned.
 * If the time is
 * less than or equal to zero, the method will not wait at all.
 *
 * <p><b>Implementation Considerations</b>
 *
 * <p>The ability to interrupt a lock acquisition in some implementations
 * may not be possible, and if possible may
 * be an expensive operation.
 * The programmer should be aware that this may be the case. An
 * implementation should document when this is the case.
 *
 * <p>An implementation can favor responding to an interrupt over normal
 * method return, or reporting a timeout.
 *
 * <p>A {@code Lock} implementation may be able to detect
 * erroneous use of the lock, such as an invocation that would cause
 * deadlock, and may throw an (unchecked) exception in such circumstances.
 * The circumstances and the exception type must be documented by that
 * {@code Lock} implementation.
 *
 * @param time the maximum time to wait for the lock
 * @param unit the time unit of the {@code time} argument
 * @return {@code true} if the lock was acquired and {@code false}
 *         if the waiting time elapsed before the lock was acquired
 *
 * @throws InterruptedException if the current thread is interrupted
 *         while acquiring the lock (and interruption of lock
 *         acquisition is supported)
 */
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

调用该方法将在给定等待时间内尝试获取锁(支持线程中断响应)。若锁空闲则立即获取,返回true;若锁不可用,则出于线程调度目的,当前线程将被禁止执行并进入休眠状态,直至以下情形之一发生:

(1)当前线程成功获取锁;

(2)其他线程中断当前线程且锁实现支持获取过程的中断;

(3)超过指定等待时间。

若当前线程:

(1)进入此方法时已被中断;

(2)在获取锁过程中被中断且支持中断响应;

则抛出被中断异常InterruptedException并清除中断状态。若超过指定等待时间则返回false。若等待时间≤0,则此方法将完全不等待。实现注意事项同lock()方法。

参数与返回值说明:

time指定等待锁的最大时间;

unit 指定时间参数的单位;

返回true表示成功获取锁,返回false表示等待超时。

5、unlock()

/**
 * Releases the lock.
 *
 * <p><b>Implementation Considerations</b>
 *
 * <p>A {@code Lock} implementation will usually impose
 * restrictions on which thread can release a lock (typically only the
 * holder of the lock can release it) and may throw
 * an (unchecked) exception if the restriction is violated.
 * Any restrictions and the exception
 * type must be documented by that {@code Lock} implementation.
 */
void unlock();

调用该方法线程将释放锁,此方法必须有持有锁的线程调用。实现注意事项:Lock的实现通常会对释放锁的线程施加限制,典型情况下仅锁持有者能释放锁),若违反限制可能抛出(非受检)异常。具体限制及异常类型必须由实现类说明。

6、newCondition()

/**
 * Returns a new {@link Condition} instance that is bound to this
 * {@code Lock} instance.
 *
 * <p>Before waiting on the condition the lock must be held by the
 * current thread.
 * A call to {@link Condition#await()} will atomically release the lock
 * before waiting and re-acquire the lock before the wait returns.
 *
 * <p><b>Implementation Considerations</b>
 *
 * <p>The exact operation of the {@link Condition} instance depends on
 * the {@code Lock} implementation and must be documented by that
 * implementation.
 *
 * @return A new {@link Condition} instance for this {@code Lock} instance
 * @throws UnsupportedOperationException if this {@code Lock}
 *         implementation does not support conditions
 */
Condition newCondition();

调用该方法将返回一个Condition类型的条件实例,该实例绑定到Lock实例中。在条件上等待前,当前线程必须持有该锁。调用 Condition的await() 将在等待前原子性地释放锁,并在等待返回前重新获取锁。实现注意事项:Condition实例的具体行为取决于Lock实现,必须由实现类说明。

返回值说明:返回此锁实例的新条件对象。

异常说明:若此锁实现不支持条件变量将抛出UnsupportedOperationException 。

Lock优势举例

以下是Lock接口的典型使用场景及相比synchronized的优势说明:

1、可中断锁获取‌

synchronized阻塞时无法中断,Lock可通过lockInterruptibly()实现:

public boolean trySendMessage(Lock lock, Message msg) {
    try {
        //尝试获取锁,最多等待3秒
        if (lock.tryLock(3, TimeUnit.SECONDS)) {  
            try {
                return send(msg);
            } finally {
                //解锁
                lock.unlock();
            }
        }
        //捕获中断异常
    } catch (InterruptedException e) {
        //中断当前线程
        Thread.currentThread().interrupt();
    }
    // 超时处理
    return false;  
}

当线程在synchronized块内阻塞时,调用interrupt()无效,而Lock允许中断等待线程。

2、尝试非阻塞获取锁‌

上面的例子中,通过tryLock()方法实现快速失败机制,超过指定时间还没有获取到锁则立即返回。可以有效避免无限死等待的问题,而synchronized无法实现这种"尝试获取锁,失败立即返回"的逻辑。

3、锁等待超时‌

上面的例子中tryLock()方法可以指定获取锁的等待时间,避免无限等待,而synchronized无法设置获取锁的超时时间。

4、公平的抢锁实现‌

公平锁的实现由Lock接口的具体实现类来实现,比如ReentrantLock,可以通过构造函数的fair参数指定是否创建公平锁,创建公平锁后,抢锁的线程必须安装先来先抢的原则排队抢锁,可以有效避免线程长时间抢不到锁的问题,而synchronized仅支持非公平锁,可能导致线程饥饿。

public class FairService {
	// 关键点:创建一个公平锁
    private Lock fairLock = new ReentrantLock(true);  
    public void service() {
        fairLock.lock();
        try {
            // 先请求锁的线程先获得资源
        } finally {
            fairLock.unlock();
        }
    }
}

5、多等待条件

在系列文章的第二篇中,我们知道synchronized与wait()/notifyAll()方法实现的多线程并发协同,无法区分不同的等待条件,也不能唤醒指定的等待线程。而单个Lock对象可以通过调用newCondition()方法创建多个Condition等等条件实现更精细通知与唤醒。

 public class BoundedBuffer {
    final Lock lock = new ReentrantLock();
     //队列未满条件1
    final Condition notFull = lock.newCondition();  
     //队列非空条件2
    final Condition notEmpty = lock.newCondition(); 
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
             // 等待非满条件
            while (isFull()) notFull.await(); 
            // 入队操作
            ...
            // 唤醒非空条件
            notEmpty.signal();  
        } finally {
            lock.unlock();
        }
    }
}

核心方法与关键优势总结

核心方法总结

核心方法功能描述关键特性适用场景
void lock()阻塞式获取锁若锁被占用则线程阻塞,直到获取锁为止必须确保获取锁的场景
boolean tryLock()尝试非阻塞获取锁立即返回获取结果(成功true/失败false)快速失败机制、避免死锁
boolean tryLock(long time, TimeUnit unit)超时尝试获取锁在指定时间内等待锁,超时返回false带超时控制的资源竞争
void lockInterruptibly()可中断式获取锁等待过程中响应线程中断请求需要中断响应的长时间等待
void unlock()释放锁必须由锁持有者调用,否则抛异常必须配合finally块使用
Condition newCondition()创建条件变量实现多等待队列(如生产者-消费者)精细化的线程通信

关键优势总结

(1)响应中断能力(避免死锁)
(2)非阻塞尝试机制(提升系统响应)
(3)公平锁支持(解决线程饥饿)
(4)多条件变量(精细线程通信)
(5)超时等待控制(系统健壮性保障)
这些特性使得Lock在某些更复杂的场景中成为不可替代的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帧栈

您的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值