本章主要通过解读Lock接口的源码,来学习Lock接口定义的方法的使用。
1.源码注释Lock接口,定义了如下方法:
/** * Lock接口 * @since 1.5 * @author Doug Lea */ public interface 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. */ void lock(); /** * Acquires the lock unless the current thread is * {@linkplain Thread#interrupt interrupted}. * * <p>Acquires the lock if it is available and returns immediately. * * <p>If the lock is not available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two 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. * </ul> * * <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. */ void lockInterruptibly() throws InterruptedException; /** * 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(); /** * 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. */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * 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(); /** * 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. */ Condition newCondition(); }
通过解读上面的源码,将Lock接口提供的方法总结如下:
1. void lock();
- 获得锁
-
如果锁不可用,那么当前线程将被禁用以实现线程调度,并处于休眠状态,直到获得锁。
2.void lockInterruptibly() throws InterruptedException;
- 获取锁,除非当前线程被中断。
- 如果锁可用,则获取锁并立即返回。
- 如果锁不可用,那么当前线程将被禁用,以进行线程调度,并处于休眠状态,直到发生以下两种情况之一:
- 当前线程获取锁;
- 其他一些线程中断当前线程,并且支持中断获取锁。
- 如果当前线程:
- 在进入该方法时设置中断状态;
- 在获取锁时中断,并且支持获取锁的中断,
- 然后抛出InterruptedException,并清除当前线程的中断状态。
3.boolean tryLock();
- 只有在调用时锁是空闲的,才获取锁。
- 如果锁空闲,则获取锁,并立即返回true值。
- 如果锁不可用,则立即返回false值。
- tryLock()方法的典型使用方式如下:
Lock lock = ...; if (lock.tryLock()) {//如果锁空闲,就获取锁 try { // manipulate protected state } finally { lock.unlock();//获取了锁就要记得释放 } } else {//没获取锁,那就干点别的 // perform alternative actions }
- 上述方式能够保证:如果获取了锁能够释放锁;如果没获取锁,也不会去尝试释放锁。
4.boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
- 如果在限定时间内,锁可用并且当前线程不被中断,则获取锁。
- 如果锁空闲,则获取锁,并立即返回true值。
- 如果锁不可用,则为了线程调度的目的,当前线程会变得不可用,直到出现以下三种情况之一:
- 当前线程获取锁。
- 其他的线程中断了这个线程。
- 限定时间超时。
- 针对上面三种情况,当前方法会分别作出以下操作:
- 如果获得锁,则即返回true值。
- 如果当前线程在获取锁操作中,被其他线程中断,则会抛出InterruptedException异常,并且将中断标识清除。
- 如果限定时间超时,则即返回false值。
5.void unlock();
- 解锁,或者叫释放锁。
- 通常这样释放锁:
//获取锁 try { // 共享资源处理 } finally { l.unlock();//释放锁 }
6.Condition newCondition();
- 获取一个绑定到当前Lock对象的Condition对象。
- 获取Condition对象的前提是当前线程持有Lock对象。
关于Condition的相关内容会在下个章节中进行学习。
3.实例代码
实例场景:
- 定义5个线程。
- [Thread-0]刚开始就获取了锁,并且会持有锁5000毫秒才会解锁。
- [Thread-1]通过lock.lock()去获取锁,获取锁之后会持有10毫秒然后释放。
- [Thread-2]通过lock.tryLock()去获取锁,获取锁之后会持有10毫秒然后释放。
- [Thread-3]通过lock.tryLock(long,TimeUnit)尝试在2秒内去获取锁,获取锁之后会持有10毫秒然后释放。
- [Thread-4]通过lock.lockInterruptibly()去获取锁,获取锁之后会持有10毫秒然后释放。
- 在所有线程启动3000毫秒后,中断[Thread-4]。
结果预测:
通过上面对方法的解读,结合实例场景,可以推断程序执行结果如下:
- 刚开始,[Thread-0]就获得了锁。
- [Thread-1]通过lock.lock()去获取锁,因为这种操作是阻塞的、不可中断、不可超时的,所以它会一直等待,直到[Thread-0]释放锁。
- [Thread-2]通过lock.tryLock()去获取锁,因为当时锁被占用,所以没有获取锁,[Thread-2]不再尝试去获取锁。
- [Thread-3]通过lock.tryLock(long,TimeUnit)尝试在2秒内去获取锁,因为超时时间2秒短于5000毫秒,所以[Thread-3]没有获取锁,并在超时之后不再尝试去获取锁。
- [Thread-4]通过lock.lockInterruptibly()去获取锁,并且在3000毫秒时被main线程中断,所以[Thread-4]没有获取锁,并在被中断之后不再尝试去获取锁。
实例代码:
根据实例场景编写代码如下:
/** * <p>Lock接口-方法学习-可中断锁、可定时锁</p> * * @author hanchao 2018/3/18 13:58 **/ public class LockDemo { //定义一个非公平的锁 private static Lock lock = new ReentrantLock(false); /** * <p>Lock接口方法学习</p> * * @author hanchao 2018/3/18 13:54 **/ public static void main(String[] args) throws InterruptedException { //线程0一直持有锁5000毫秒 new Thread(() -> { System.out.println("线程[" + Thread.currentThread().getName() + "]尝试获取锁"); lock.lock(); System.out.println("线程[" + Thread.currentThread().getName() + "]获取了锁..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally {//在finally代码块中是否锁 lock.unlock(); System.out.println("线程[" + Thread.currentThread().getName() + "]释放了锁.."); } }).start(); Thread.sleep(10); //线程1通过lock.lock()持续去尝试获取锁 new Thread(() -> { System.out.println("线程[" + Thread.currentThread().getName() + "]通过lock.lock()持续去尝试获取锁"); lock.lock(); System.out.println("线程[" + Thread.currentThread().getName() + "]获取了锁..."); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } finally {//在finally代码块中是否锁 lock.unlock(); System.out.println("线程[" + Thread.currentThread().getName() + "]释放了锁.."); } }).start(); //线程2通过lock.tryLock()尝试去获取一次锁 new Thread(() -> { System.out.println("线程[" + Thread.currentThread().getName() + "]通过lock.tryLock()尝试去获取一次锁"); if (lock.tryLock()) { System.out.println("线程[" + Thread.currentThread().getName() + "]获取了锁..."); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println("线程[" + Thread.currentThread().getName() + "]释放了锁.."); } } else { System.out.println("线程[" + Thread.currentThread().getName() + "]尝试获取锁失败,不再等待."); } }).start(); //线程3通过lock.tryLock(long,TimeUnit)尝试在一定时间内去获取锁 new Thread(() -> { System.out.println("线程[" + Thread.currentThread().getName() + "]通过lock.tryLock(long,TimeUnit)尝试在一定时间内去获取锁"); try { if (lock.tryLock(2, TimeUnit.SECONDS)) { System.out.println("线程[" + Thread.currentThread().getName() + "]获取了锁..."); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println("线程[" + Thread.currentThread().getName() + "]释放了锁.."); } } else { System.out.println("线程[" + Thread.currentThread().getName() + "]在指定时间内没有获取到锁,不再等待."); } } catch (InterruptedException e) { //e.printStackTrace(); 被中断时会产生的意料之中的错误,无需打印 System.out.println("线程[" + Thread.currentThread().getName() + "]被thread.interrupt()中断,不在尝试去获取锁"); } }).start(); //线程4通过lock.lockInterruptibly()尝试可中断的去获取锁 Thread thread4 = new Thread(() -> { try { System.out.println("线程[" + Thread.currentThread().getName() + "]通过lock.lockInterruptibly()尝试可中断的去获取锁"); lock.lockInterruptibly(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); System.out.println("线程[" + Thread.currentThread().getName() + "]释放了锁.."); } } catch (InterruptedException e) { //e.printStackTrace(); 被中断时会产生的意料之中的错误,无需打印 System.out.println("线程[" + Thread.currentThread().getName() + "]被thread.interrupt()中断,不在尝试去获取锁"); } }); thread4.start(); Thread.sleep(3000); thread4.interrupt(); } }
运行结果:
线程[Thread-0]尝试获取锁 线程[Thread-0]获取了锁... 线程[Thread-2]通过lock.tryLock()尝试去获取一次锁 线程[Thread-4]通过lock.lockInterruptibly()尝试可中断的去获取锁 线程[Thread-3]通过lock.tryLock(long,TimeUnit)尝试在一定时间内去获取锁 线程[Thread-1]通过lock.lock()持续去尝试获取锁 线程[Thread-2]尝试获取锁失败,不再等待. 线程[Thread-3]在指定时间内没有获取到锁,不再等待. 线程[Thread-4]被thread.interrupt()中断,不在尝试去获取锁 线程[Thread-1]获取了锁... 线程[Thread-0]释放了锁.. 线程[Thread-1]释放了锁..
运行结果与预估一样。
4.总结下面对上述的5个方法进行一句话总结:
- lock():获取锁,不可中断、不可超时。
- lockInterruptibly():获取锁,可以中断、不可超时。
- trylock():获取当前可用的锁并返回true,否则返回false,无需中断、无需超时。
- tryLock(long time, TimeUnit unit):获取限定时间可用的锁并返回true,否则返回false,可以中断、可以超时。
- unlock():解锁。