ReentrantLock的使用
构造方法
构造方法名 | 说明 |
---|---|
ReentrantLock() | 创建一个非公平锁 |
ReentrantLock(boolean fair) | 以给定的公平策略创建一个ReentrantLock的实例 |
-
公平锁:公平锁能保证先获取锁的线程一定能先得到锁,避免饥饿,效率不是太高
-
非公平锁:非公平锁不保证谁先获取谁先得到锁,会产生饥饿线程,效率高
实例方法
实例方法名 | 说明 |
---|---|
int getHoldCount() | 返回当前线程获取此锁的次数 |
Thread getOwner() | 返回当前拥有此锁的线程,如果不存在,则返回null |
Collection getQueuedThreads() | 返回同步队列中等待获取此锁的线程的集合 |
int getQueueLength() | 返回同步队列中等待获此锁的线程数量 |
Collection getWaitingThreads(Condition condition) | 返回与此锁相关联的给定条件下等待的线程的集合 |
int getWaitQueueLength(Condition condition) | 返回与此锁相关联的给定条件等待的线程数 |
boolean hasQueuedThread(Thread thread) | 查询给定线程是否等待获取此锁 |
boolean hasQueuedThreads() | 查询是否有线程正在等待获取此锁。 |
boolean hasWaiters(Condition condition) | 查询是否有线程等待与此锁相关联的给定条件 |
boolean isFair() | 如果此锁的公平设置为true,则返回true |
boolean isHeldByCurrentThread() | 查询此锁是否由当前线程持有 |
boolean isLocked() | 查询此锁被占用。 |
void lock() | 获取锁,不可中断 |
void lockInterruptibly() | 获取锁,可中断 |
Condition newCondition() | 创建与此锁相关的Condition,每调用一次创建一个 |
boolean tryLock() | 尝试获取锁 |
boolean tryLock(long timeout, TimeUnit unit) | 如果在给定的等待时间内尝试获取锁 |
void unlock() | 释放锁 |
ReentrantLock的使用
实现synchronized的同步功能
package com.morris.concurrent.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private static int count = 10;
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
new A().start();
new A().start();
new A().start();
}
static class A extends Thread {
@Override
public void run() {
while (true) {
lock.lock();
try {
if (0 == count) {
break;
}
System.out.println(count--);
} finally {
lock.unlock();
}
}
}
}
}
实现wait-notify的等待-通知功能
Condition类的await()方法相当于Object类的wait()方法。
Condition类的await(long time, TimeUnit unit)方法相当于Object类的wait(long timeout)方法。
Condition类的signal()方法相当于Object类的notify()方法。
Condition类的signalAll()方法相当于Object类的notifyAll()方法。
package com.morris.concurrent.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionDemo {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
A a = new A();
a.start();
Thread.sleep(1000);
a.signal();
}
static class A extends Thread {
@Override
public void run() {
await();
}
public void await() {
lock.lock();
try {
try {
System.out.println("thread A is waiting...");
condition.await();
System.out.println("thread A is stoped");
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
public void signal() {
lock.lock();
try {
System.out.println("notify thread a...");
condition.signal();
} finally {
lock.unlock();
}
}
}
}
一个锁对应多个Condition
在多个生产者,多个消费者模型中,如果使用synchronized的wait-notify,在唤醒线程时要使用notifyAll()方法,因为有可能唤醒的是同类线程(本身是生产者线程,用notify唤醒了另一个生产者线程),会造成假死。
如果使用Lock的Condition中的await-signal,可以使用多个Condition,这样在唤醒时可以有针对的唤醒需要唤醒的线程的类型,不会造成不必要的锁的竞争。
package com.morris.concurrent.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MoreConditionDemo {
private static Lock lock = new ReentrantLock();
private static String value;
private static Condition conditioP = lock.newCondition();
private static Condition conditioC = lock.newCondition();
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
new Provider().start();
new Consumer().start();
}
}
static class Provider extends Thread {
@Override
public void run() {
lock.lock();
try {
while (true) {
if (null == value) {
value = Thread.currentThread().getName() + " " + System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + " set value:" + value);
conditioC.signal();
} else {
conditioP.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
static class Consumer extends Thread {
@Override
public void run() {
lock.lock();
try {
while (true) {
if (null != value) {
System.out.println(Thread.currentThread().getName() + " get value:" + value);
value = null;
conditioP.signal();
} else {
conditioC.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
ReentrantLock重入锁源码分析
ReentrantLock重入锁,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁的还支持获取锁时的公平和非公平性选择。
ReentrantLock内置了一个公平锁和非公平锁。
实现可重入
重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞,该特性的实现需要解决以下两个问题。
-
线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。
-
锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经成功释放。
锁的获取:
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; // 没获取到锁
}
锁的释放:
protected final boolean tryRelease(int releases) {
int c = getState() - releases; // 减去锁的次数
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // 同步状态为0,则释放锁
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
实现公平
公平与非公平的区别具体体现在获取锁的过程中:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 首先判断一下队列中是否有比当前线程等待时间更长的线程,如果有则直接进入同步队列
if (!hasQueuedPredecessors() && // 公平锁加锁与非公平锁加锁就这行代码不同
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
// 判断一下队列中是否有比当前线程等待时间更长的线程
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}