您的位置:首页 > 编程语言 > Java开发

Java并发19:Lock系列-Lock接口基本方法学习实例

2018-03-24 18:53 711 查看
[超级链接:Java并发学习系列-绪论]

Lock接口在之前的章节中多次提及:

Java并发02:Java并发Concurrent技术发展简史(各版本JDK中的并发技术)

Java并发12:并发三特性-原子性、可见性和有序性概述及问题示例

Java并发13:并发三特性-原子性定义、原子性问题与原子性保证技术

Java并发14:并发三特性-可见性定义、可见性问题与可见性保证技术

Java并发15:并发三特性-有序性定义、有序性问题与有序性保证技术

Java并发18:Lock接口与synchronized关键字的比较

本章主要通过解读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();
}


2.源码解读

通过解读上面的源码,将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():解锁。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐