java.util.concurrent.locks public interface Lock
Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects.
A lock is a tool for controlling access to a shared resource by multiple threads. Commonly, a lock provides exclusive access to a shared resource: only one thread at a time can acquire the lock and all access to the shared resource requires that the lock be acquired first. However, some locks may allow concurrent access to a shared resource, such as the read lock of a ReadWriteLock.
The use of synchronized methods or statements provides access to the implicit monitor lock associated with every object, but forces all lock acquisition and release to occur in a block-structured way: when multiple locks are acquired they must be released in the opposite order, and all locks must be released in the same lexical scope in which they were acquired.
While the scoping mechanism for synchronized methods and statements makes it much easier to program with monitor locks, and helps avoid many common programming errors involving locks, there are occasions where you need to work with locks in a more flexible way. For example, some algorithms for traversing concurrently accessed data structures require the use of "hand-over-hand" or "chain locking": you acquire the lock of node A, then node B, then release A and acquire C, then release B and acquire D and so on. Implementations of the Lock interface enable the use of such techniques by allowing a lock to be acquired and released in different scopes, and allowing multiple locks to be acquired and released in any order.
With this increased flexibility comes additional responsibility. The absence of block-structured locking removes the automatic release of locks that occurs with synchronized methods and statements. In most cases, the following idiom should be used:
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
When locking and unlocking occur in different scopes, care must be taken to ensure that all code that is executed while the lock is held is protected by try-finally or try-catch to ensure that the lock is released when necessary.
Lock implementations provide additional functionality over the use of synchronized methods and statements by providing a non-blocking attempt to acquire a lock (tryLock()), an attempt to acquire the lock that can be interrupted (lockInterruptibly, and an attempt to acquire the lock that can timeout (tryLock(long, TimeUnit)).
A Lock class can also provide behavior and semantics that is quite different from that of the implicit monitor lock, such as guaranteed ordering, non-reentrant usage, or deadlock detection. If an implementation provides such specialized semantics then the implementation must document those semantics.
Note that Lock instances are just normal objects and can themselves be used as the target in a synchronized statement. Acquiring the monitor lock of a Lock instance has no specified relationship with invoking any of the lock methods of that instance. It is recommended that to avoid confusion you never use Lock instances in this way, except within their own implementation.
Except where noted, passing a null value for any parameter will result in a NullPointerException being thrown.
Memory Synchronization
All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock, as described in The Java Language Specification (17.4 Memory Model) :
A successful lock operation has the same memory synchronization effects as a successful Lock action.
A successful unlock operation has the same memory synchronization effects as a successful Unlock action.
Unsuccessful locking and unlocking operations, and reentrant locking/unlocking operations, do not require any memory synchronization effects.
Implementation Considerations
The three forms of lock acquisition (interruptible, non-interruptible, and timed) may differ in their performance characteristics, ordering guarantees, or other implementation qualities. Further, the ability to interrupt the ongoing acquisition of a lock may not be available in a given Lock class. Consequently, an implementation is not required to define exactly the same guarantees or semantics for all three forms of lock acquisition, nor is it required to support interruption of an ongoing lock acquisition. An implementation is required to clearly document the semantics and guarantees provided by each of the locking methods. It must also obey the interruption semantics as defined in this interface, to the extent that interruption of lock acquisition is supported: which is either totally, or only on method entry.
As interruption generally implies cancellation, and checks for interruption are often infrequent, an implementation can favor responding to an interrupt over normal method return. This is true even if it can be shown that the interrupt occurred after another action may have unblocked the thread. An implementation should document this behavior.
Since:
1.5
See Also:
ReentrantLock, Condition, ReadWriteLock
< 1.8 >
与使用同步方法和语句相比,Lock实现提供了更广泛的锁操作。它们允许更灵活的结构,可以具有完全不同的属性,并且可以支持多个关联的Condition对象。
Lock是一种用于控制多个线程对共享资源的访问的工具。 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取该锁,对共享资源的所有访问都需要首先获取该锁。但是,某些锁可能允许并发访问共享资源,例如ReadWriteLock的读取锁。
使用同步方法或语句可访问与每个对象关联的隐式监视器锁,但强制所有锁的获取和释放以块结构的方式发生:当获取多个锁时,它们必须以相反的顺序释放,并且所有锁必须在获得它们的相同词汇范围内释放。
尽管用于同步方法和语句的作用域机制使使用监视器锁的编程变得容易得多,并且有助于避免许多常见的涉及锁的编程错误,但在某些情况下,您需要以更灵活的方式使用锁。例如,某些用于遍历并发访问的数据结构的算法需要使用“hand-over-hand”或“chain olcking”:您获取节点A的锁,然后获取节点B的锁,然后释放A并获取C,然后释放B并获得D等。 (但是synchronized难以实现)。锁接口的实现通过允许在不同的作用域中获取和释放锁,并允许以任何顺序获取和释放多个锁,从而允许使用此类技术。
随着灵活性的增加,来了更多的责任。缺少块结构锁定移除了synchronized方法或语句块会自动
获得和释放锁的能力。在大多数情况下,应使用一下习语:
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
当锁定和解锁发生在不同的范围内时,必须小心,确保在锁定时执行的所有代码都在try-catch
-finally受保护的,以确保在必要时释放锁。锁实现通过提供在synchronized方法及语句块
之上的非阻塞方法tryLock()方法来获得锁,试图获取锁可以被中断,并且可能超时tryLock(long,
TimeUnit)
Lock类还可以提供与隐式监视器锁完全不同的行为和语义。(如保证排序,不可重入用法或死锁检测)
如果实现提供了此专用语义,那么实现必须记录这些语义。
请注意,锁实例只是普通对象,并且可以作为同步语句块中的目标使用。获取锁实例的监视器与调用
该实例的任何lock()方法没有指定的关系。建议避免混乱,你永远不会这样使用锁实例,除非在它们
的实现中。
还有,为任何参数传递null将导致空指针异常。
内存同步
所有的锁实现必须强制执行与内置监视器锁提供的相同内存同步语义。
不成功的加锁或解锁操作以及可重入的加锁和解锁操作,不需要保证任何内存同步效果。
将要实现的原则
三种形式的锁获取(可中断、不可中断和定时)可能不同于其性能特点,顺序保证及其他实现质量。此外,在给定的锁类中可能无法使用中断正在进行的锁获取的能力,因此,不需要实现对所有三种形式的锁获取完全相同的保证或语义,也不需要支持正在进行的锁获取中断。需要实现以清楚地记录每个锁定方法提供的语义和保证。它还必须遵循此接口中定义的中断语义, 优劣支持锁获取中断: 这要么完全, 或仅在方法项上。
由于中断通常意味着取消,并且通常不频繁地检查中断,因此实现可以支持通过寻常方法返回对
一个中断的相应。当中断发生在另一个操作阻塞线程也成立。
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
通过lock()方法获取锁
如果锁已被其他线程获取,则进行等待。如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此,一般来说,使用Lock必须在try…catch…块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。通常使用Lock来进行同步的话,是以下面这种形式去使用:
Lock l = ...;
l.lock(); // 加锁
try {
// access the resource protected by this lock
} finally {
l.unlock(); // 释放锁
}
lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。例如,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出 InterruptedException,但推荐使用后者。因此,lockInterruptibly()一般的使用形式如下:
public void method() throws InterruptedException {
lock.lockInterruptibly();
try {
//.....
}
finally {
lock.unlock();
}
}
注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为interrupt()方法只能中断阻塞过程中的线程而不能中断正在运行过程中的线程。因此,当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,那么只有进行等待的情况下,才可以响应中断的。与 synchronized 相比,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true;如果获取失败(即锁已被其他线程获取),则返回false,也就是说,这个方法无论如何都会立即返回(在拿不到锁时不会一直在那等待)。
tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false,同时可以响应中断。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
一般情况下,通过tryLock来获取锁时是这样使用的:
Lock lock = ...;
if(lock.tryLock()) {
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}
释放锁
在finally块中执行。
ReentrantLock,即可重入锁。ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。
构造方法(不带参数 和带参数 true: 公平锁; false: 非公平锁):
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReadWriteLock 接口只有两个方法:
/**
* Returns the lock used for reading.
*
* @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing
*/
Lock writeLock();
ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个reader 线程同时保持,而写入锁是独占的。
package com.learn.thread.rwlock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockTest {
private ReadWriteLock rwLock = new ReentrantReadWriteLock();
public void readMethod() {
rwLock.readLock().lock();
try {
for (int i = 0; i < 3; ++i) {
try {
Thread.sleep((long) (100 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在读文件。。。");
}
} finally {
System.out.println("释放读锁。。。");
rwLock.readLock().unlock();
}
}
public void writeMethod() {
rwLock.writeLock().lock();
try {
for (int i = 0; i < 3; ++i) {
try {
Thread.sleep((long) (100 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在写文件。。。");
}
} finally {
System.out.println("释放写锁。。。");
rwLock.writeLock().unlock();
}
}
public static void main(String[] args) {
ReadWriteLockTest rwt = new ReadWriteLockTest();
for (int i = 1; i < 4; ++i) {
new Thread(() -> { rwt.readMethod(); }, "读线程" + i).start();
}
for (int i = 1; i < 3; ++i) {
new Thread(() -> { rwt.writeMethod(); }, "写线程" + i).start();
}
}
}
本文地址:https://blog.csdn.net/wz122330/article/details/107493354
如对本文有疑问, 点击进行留言回复!!
NullPointerException: Attempt to invoke virtual method ‘android.content.res.XmlResourceParser androi
关于启动appium-desktop,报错:Cannot extract apk info using apkanalyzer. Falling back to aapt. Original ....
Gradle 发布共享库——如何通过Gradle发布Android依赖库(aar)到 jitpack 公共仓库
Gradle 发布共享库——如何通过Gradle发布java依赖库(jar)到 jitpack 公共仓库(—)
网友评论