Java中Lock(synchronized和lock区别)
首先我们要明白,lock锁是后出的(1.5),肯定是弥补了部分的不足,一起认识下他们的区别
- synchronized是内置的关键字(功能统一),Lock锁是一个类,在java.util.concurrent.locks下的(功能可以改变的)。
- 由于synchronized是悲观锁,他会一直等等待对象头锁信息,阻塞的,不可被打断的。而Lock可以判断是否获取锁,并且可以不一定等待获取到锁。可以结束自己
- synchronized是自动上锁和自动释放锁,他会在编译class的时候产生两句原语:mintorenter,minorexit,代表上锁和推出锁。而Lock锁更像1.5之后Jmm中新增的lock和unLock(解决工作内存和主内存数据不一致),必须手动释放锁和加锁。
- synchronized是可重入的锁,不可以中断的,非公平的(处于性能考虑),Lock是可以决定重入锁,可以设置公平性,可以判断锁状态,如果没有能力获取锁则不会死锁,不会一直等待获取锁。
- synchronized是重量级锁,不适合大量代码。Lock可以,lock锁的粒度更细,分为读锁写锁等。
synchronized和Lock(官方注释:在Lock这个类中)
我们从java.util.concurrent.locks.Lock这个类的类注释学习
/* {@code Lock} implementations provide more extensive locking
* operations than can be obtained using {@code synchronized} methods
* and statements. They allow more flexible structuring, may have
* quite different properties, and may support multiple associated
* {@link Condition} objects.
翻译
我们实现Lock接口的锁讲比synchronized提供更灵活的声明和方法 (声明可以是公平或非公平,方法可以新增tryLock)
Lock允许更灵活的结构,完全不同的属性,并且可以关联多个对象(synchronized的属性和方法是规定好的)
/* <p>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 {@link ReadWriteLock}.
翻译
锁是什么东西?锁是多线程环境下,对共享资源的控制访问的一种工具
通常,锁提供对以下内容的资源的独占访问权
同一时间内,这个锁只允许第一个获的它的人,去访问共享资源
但是,有一些锁允许并发的访问资源。如读写锁(这里Lock把锁粒度细分了)
/* <p>The use of {@code 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.
*
翻译
使用synchronized关键词可提供对每个对象的监视和访问
但是强制所有的锁的获取和释放都在同步块总执行的
当获取多个锁的时候他们的释放顺序和获取顺序相反
这些锁必须在获取他们相同的语法范围内释放
/* <p>While the scoping mechanism for {@code 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
* {@code 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.
翻译
synchronized的作用域机制使得我们使用监视器锁来控制共享变量更加容易
避免我们许多编程错误(不需要我们手动上锁和释放锁)
不过在某些灵活的加锁场景下,需要灵活的方式 (可能会突破加&释放锁的范围)
例如:一些遍历算法并发访问的数据结构需要使用,移交或锁链;
你先获取节点A的锁,再获取节点B的锁,然后释放A并获取C的锁,然后释放B并获取D的锁
以此类推,如果实现lock接口,允许在不同的范围内获取锁和释放锁,允许多个锁释放可以不按照加锁的顺序
/* <p>With this increased flexibility comes additional
* responsibility. The absence of block-structured locking removes the
* automatic release of locks that occurs with {@code 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.
翻译
随着灵活性的增加,消除块结构锁(synchronized)
则移除了自动加锁和释放锁的方法和声明过程。
我们大多数可以这样实现手动加锁释放锁
Lock l = ......; // 实现Lock接口的实现类
l.lock(); // 加锁
try{
// 需要加锁的资源
}finally(){
l.lock() // 释放锁
}
当解锁和加锁在不同的范围时,
必须关心确定我们加锁的代码受try finally保护
那样我们在必要的时候可以释放锁
/* <p>{@code Lock} implementations provide additional functionality
* over the use of {@code synchronized} methods and statements by
* providing a non-blocking attempt to acquire a lock ({@link
* #tryLock()}), an attempt to acquire the lock that can be
* interrupted ({@link #lockInterruptibly}, and an attempt to acquire
* the lock that can timeout ({@link #tryLock(long, TimeUnit)}).
翻译
lock利用非阻塞的tyLock()去尝试获取锁的方法
比synchronized提供了更多的方法和声明
tryLock方式是尝试获取一个可被中断的锁
尝试获取一个超时的锁
/* <p>A {@code 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.
翻译
Lock锁可以提供的行为和语义和synchronized(monitor lock)有很大不同
例如;保证顺序,不可重入使用,死锁检测。
如果使用了这些专门的语义,则必须记录这些语义(我的理解是需要了解这些语义再使用,可以当作参数使用)
/* <p>Note that {@code Lock} instances are just normal objects and can
* themselves be used as the target in a {@code synchronized} statement.
* Acquiring the
* monitor lock of a {@code Lock} instance has no specified relationship
* with invoking any of the {@link #lock} methods of that instance.
* It is recommended that to avoid confusion you never use {@code Lock}
* instances in this way, except within their own implementation.
翻译
请注意:Lock锁也仅仅是一个普通对象的实例
也是能够被synchronized代码块包裹的
synchronized的隐式监视器和实例的lock方法并没有特殊的指定的关系
为了避免混淆,避免用这个方法使用Lock实例
/* <p>Except where noted, passing a {@code null} value for any
* parameter will result in a {@link NullPointerException} being
* thrown.
翻译
除非特殊说明,否则任何一个传入值为空引用都会抛出一个空指针的异常
Lock接口中定义的方法解读
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.
*/
如果锁不可用,则对于调度程序来所当前线程会变成禁用状态
并且休眠,直到,这个锁可用为止
<注意事项>
使用Lock锁可能去检查像死锁这样的错误
有些情况下会抛出一个异常
在这些情况下需要告诉实现者这个异常
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
*/
如果在调用的时候这个锁是空闲的才去获得这个锁
如果这个锁可以获得,则获得锁立即返回true
如果这个锁不可以获得,立马返回false
典型用法如下
Lock lock = ...;
if (lock.tryLock()) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}
此方式可以确保只有获取锁的人才能释放锁
如果没有获得锁,则不可以释放锁
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.
*/
Lock实现类可以限制哪些线程可以释放锁
通常只有持有锁的线程才能释放
违反规定的话会抛出(未检查的)异常
任何异常都需要被记录下来
还有其他方法暂不介绍
在 一定时间内获取锁:
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
可中断锁:
void lockInterruptibly() throws InterruptedException;