Lock锁是一个接口,其实现类有如下
Lock接口的模板方法有如下
不难看出,Lock锁的特质,它在加锁时,并不是像synchronize这把同步锁,它是先尝试加锁,加锁也可以设置加锁时间,并且在阻塞时,可以直接释放锁,而且提供了lockInterruptibly(),支持响应中断
简单介绍下lock锁和tryLock的区别
/**
* 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();
/**
* 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();
这两种加锁,源码是这样说的
lock.lock(),获取锁时,如果已经锁已经被其他线程进行获取,那么就会等待,在加锁成功后,如果发生异常,是不会主动释放锁,需要我们自己释放
lock.tryLock(),可以看到是有返回值的,如果加锁成功,就会返回true,如果加锁失败,就会直接返回false,不会一直等待,但也需要我们手动释放锁
大家看到,诶,怎么还有一个Condition这样一个方法,我们又想condition又是什么?
我们点开condition这个接口有大概这些方法和实现类
其实condition提供给我们最基本的 实现等待/通知机制的 API,
//就是让当前获得锁的线程进入waiting状态,同时释放锁,类似于wait()
void await() throws InterruptedException;
// 通知某个处于waiting状态的线程,继续获得锁执行,类似于notify()
void signal();
//通知所有处于waiting状态的线程,开始争抢锁,谁获得锁,继续执行
void signalAll();
实现Lock锁的锁共有两个
第一个ReentrantReadWriteLock ,也就是读写锁,读读之间是不会阻塞,但在写时都阻塞
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
private static final long serialVersionUID = -6992448646407690164L;
/** Inner class providing readlock */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;
/** Performs all synchronization mechanics */
final Sync sync;
/**
* Creates a new {@code ReentrantReadWriteLock} with
* default (nonfair) ordering properties.
*/
public ReentrantReadWriteLock() {
this(false);
}
/**
* Creates a new {@code ReentrantReadWriteLock} with
* the given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
}
写锁的获得过程
首先判断state状态位是否为0,为0代表没有线程获取锁,直接进行判断是否是公平锁,如果不是,直接获得锁,并且cas修改state的低16位为1,改为写锁状态,获取到写锁
如果state为1,表示有线程获得到锁,判断state的低16位为0,如果是,则代表此时有线程获得到读锁,那么将会判断是否公平锁,如果不是,那么将cas获取写锁,如果state的低16位不为0,则代表有线程获取到写锁,进而判断当前获得锁的线程是否为自己,如果是的话,会判断当前线程获得写锁是否超过最大次数,如果超过则抛出异常,如果不是则会判断是否是公平锁,不是的话,就会cas获取写锁
第二个锁ReentrantLock可重入锁
使用Lock()方法获得重入锁
首先在声明锁时,如果声明锁为true,则为公平锁,如果为false就是非公平锁
先说公平锁
之后在调用锁的时候,判断state状态位是否为0,为0则代表没有其他线程获得锁,
首先会判断自己是否需要排队,如果不需要排队,才会cas修改锁,修改成功就会将AQS的state状态位修改,获取锁成功
如果state的状态位为1,代表有其他线程获得了锁,之后会判断当前锁的线程是否为当前线程,如果是的话,就会判断当前获得锁的最大获得次数,超过,抛出异常,如果没超过,就会将没获得锁的线程加入到AQS对列中去,判断当前是否为首节点,且cas获取锁的状态,不是的话,就会休眠当前线程,是的话就会直接重入,加锁成功
非公平锁
和公平锁的唯一不同,在判断state为0,是空闲状态时,不会判断自己是否需要排队,直接加锁,后续便和公平锁一样
使用tryLock()方法加锁
使用tryLock()无论是否公平都会调用nonfairTryAcquire()方法,只会判断当前线程是否可以加锁,可以加锁,便直接调用cas的compareAndSetState修改,加锁,不会判断其他
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
下图给出一个非公平锁使用lock()的加锁流程 ,帮助大家理解
最后说下,tryLock()和Lock()方法的好坏吧
tryLock方法只会判断是否可以加锁,加锁失败不会等待相比较与lock方法,不需要消耗CPU更多的性能,但仍然是需要考虑实际代码业务,针对需要,仁者见仁,智者见智吧!