Java多线程编程-ReentrantLock
- 公平锁和非公平锁
- ReentrantLock相关API
- ReentrantLock.getHoldCount()
- ReentrantLock.getQueueLength()
- ReentrantLock.getWaitQueueLength(Condition condition)
- ReentrantLock.isFair()
- ReentrantLock.hasQueuedThread(Thread thread)
- ReentrantLock.hasQueuedThreads()
- ReentrantLock.hasWaiters(Condition condition)
- ReentrantLock.isLocked()
- ReentrantLock.isHeldByCurrentThread()
- ReentrantLock.lockInterruptibly()
- ReentrantLock.tryLock()和ReentrantLock.tryLock(long time,TimeUnit unit)
公平锁和非公平锁
我们在使用java多线程编程的时候,经常会用到锁机制,锁分为公平锁和非公平锁。
公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来进行分配的,先来先得先进先出的顺序进行的。
非公平锁:就是一种锁的抢占机制,是随机获取锁的,这样会有的线程一直获取不到锁,因此对于该线程来说是不公平的。
使用ReentrantLock实现公平锁
我们使用ReentrantLock时候,ReentrantLock的构造函数可以传入一个boolean类型的参数,根据不同的值返回不同的实现方式,源码如下:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
由上面的代码可以看出,默认是进行不公平的锁机制,使用new NonfairSync()进行,公平锁为new FairSync()。因此创建ReentrantLock(true)使用公平锁策略,ReentrantLock(false)的时候使用非公平锁机制,因此 Java多线程编程-ReentrantLock(1)都是使用的默认的非公平锁策略。
公平锁
public class ReentrantLockObj {
private ReentrantLock reentrantLock;
public ReentrantLockObj(boolean isFair) {
reentrantLock = new ReentrantLock(isFair);
}
public void fun() {
reentrantLock.lock();
try {
System.out.println("current thread name :" + Thread.currentThread().getName() + " get lock");
} finally {
reentrantLock.unlock();
}
}
}
运行:
public class ReentrantLockObjMain {
public static void main(String[] args) {
ReentrantLockObj reentrantLockObj = new ReentrantLockObj(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("current thread name:" + Thread.currentThread().getName() + " do running");
reentrantLockObj.fun();
}
};
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(runnable, "thread" + i));
}
for (Thread thread : threadList) {
thread.start();
}
}
}
运行结果:
我们上面创建了10个线程,每个线程依次运行,说明我们使用ReentrantLock(true)创建的是公平锁。
我们可以将上面的true变为false,我们看看运行结果:
ReentrantLockObj reentrantLockObj = new ReentrantLockObj(false);
运行结果:
从上面的可以看出,线程的执行和获取锁的事混乱的。说明使用ReentrantLock(false)创建的非公平锁的使用。
ReentrantLock相关API
我们在使用ReentrantLock类进行锁操作的时候,只是用到了lock和unlock方法,我们看看源码:
lock():获取锁,如果没有其他线程持有该锁,则获取该锁并立即返回,将锁保持计数设置为1.如果当前线程已经持有该锁,则持有计数将增加一,并且该方法将立即返回。如果该锁由另一个线程持有,则出于线程调度目的,当前线程将被禁用,并且在获取该锁之前,该线程处于休眠状态,这时将锁持有计数设置为1。
public void lock() {
sync.lock();
}
sync对象是一个Sync接口,实现了Lock,根据是否为公平锁调用不同的实现,非公平锁如下:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
公平锁如下:
final void lock() {
acquire(1);
}
ReentrantLock.getHoldCount()
getHoldCount():查询当前线程对该锁的保持次数(调用lock方法的次数)。对于每个未与解锁动作匹配的锁定动作,线程都会拥有一个锁。保留计数信息通常仅用于测试和调试目的.
源码:
public int getHoldCount() {
return sync.getHoldCount();
}
//sync调用的方法
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
验证:
public class ReentrantLockAPIMain1 {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
testFun1();
}
public static void testFun1() {
reentrantLock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
testFun2();
} finally {
reentrantLock.unlock();
}
}
public static void testFun2() {
reentrantLock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
} finally {
reentrantLock.unlock();
}
}
}
运行结果:
上面是第一行是testFun1方法调用的,第二行是方法testFun2打印的,调用两次lock方法
ReentrantLock.getQueueLength()
getQueueLength():返回等待获取此锁的线程数的估计值。该值只是一个估计值,因为在此方法遍历内部数据结构时,线程数可能会动态变化。此方法设计用于监视系统状态,而不用于同步控制。
源码:
public final int getQueueLength() {
return sync.getQueueLength();
}
//sync调用的方法 AbstractQueuedSynchronizer.Node
/**
* 要加入CLH锁,您可以自动将其作为新尾部进行拼接。要出队,您只需设置头字段。
+------+ prev +-----+ +-----+
head | | <---- | | <---- | | tail
+------+ +-----+ +-----+ **/
public final int getQueueLength() {
int n = 0;
for (Node p = tail; p != null; p = p.prev) {
if (p.thread != null)
++n;
}
return n;
}
验证:
public class ReentrantLockAPIMain2 {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
testFun1();
}
};
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(runnable, "thread" + i));
}
threadList.stream().forEach(thread -> {
thread.start();
});
Thread.sleep(5000);
System.out.println("wait lock number:" + reentrantLock.getQueueLength());
}
public static void testFun1() {
reentrantLock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}
运行结果:
我们创建了10个线程,在一个线程中进行等待,剩余的9个线程正在等待获取锁,程序还没有结束。
ReentrantLock.getWaitQueueLength(Condition condition)
getWaitQueueLength(Condition condition):返回在与此锁关联的给定条件下等待的线程数的估计值。请注意,由于超时和中断可能随时发生,因此估算值仅用作实际侍者数的上限。此方法设计用于监视系统状态,而不用于同步控制。
源码:
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
//sync调用的方法
public final int getWaitQueueLength(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
return condition.getWaitQueueLength();
}
验证:
public class ReentrantLockAPIMain3 {
private static ReentrantLock reentrantLock = new ReentrantLock();
private static Condition condition = reentrantLock.newCondition();
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
testFun1();
}
};
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(runnable, "thread" + i));
}
threadList.stream().forEach(thread -> {
thread.start();
});
Thread.sleep(5000);
testFun2();
}
public static void testFun1() {
reentrantLock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public static void testFun2() {
reentrantLock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " reentrantLock.getWaitQueueLength(condition) number:"
+ reentrantLock.getWaitQueueLength(condition));
condition.signal();
} finally {
reentrantLock.unlock();
}
}
}
运行结果:
这里使用同一个condition,所以获取的个数为10,如果创建多个condition的,和这个差不多,是针对不同condition进行统计。我们从上面的源码可以看出,如果不传condition的时候将抛出空指针异常。
ReentrantLock.isFair()
isFair():返回是否为公平锁
源码:
public final boolean isFair() {
return sync instanceof FairSync;
}
有创建的源码可以看出,公平和非公平锁是根据sync 的不同实现来获得的。因此判断sync的实现是否为公平实现还是非公平实现即可。
ReentrantLock.hasQueuedThread(Thread thread)
hasQueuedThread(Thread thread):查询给定线程是否正在等待获取此锁。请注意,由于取消可能随时发生,因此返回{@code true}并不能保证该线程将获得此锁。此方法主要设计用于监视系统状态。
源码:
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
//调用sync方法 AbstractQueuedSynchronizer.hasQueuedThreads
public final boolean isQueued(Thread thread) {
if (thread == null)
throw new NullPointerException();
for (Node p = tail; p != null; p = p.prev)
if (p.thread == thread)
return true;
return false;
}
验证:
public class ReentrantLockAPIMain4 {
private static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
testFun1();
}
};
Thread thread = new Thread(runnable, "thread01");
thread.start();
Thread.sleep(5000);
Thread thread1 = new Thread(runnable, "thread02");
thread1.start();
Thread.sleep(5000);
System.out.println(reentrantLock.hasQueuedThread(thread));
System.out.println(reentrantLock.hasQueuedThread(thread1));
}
public static void testFun1() {
reentrantLock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
}
运行结果:
从运行可以看出,线程1正在等待。
ReentrantLock.hasQueuedThreads()
hasQueuedThreads():查询是否有任何线程正在等待获取此锁。请注意,由于取消可能随时发生,因此返回true并不能保证任何其他线程都会获得此锁。此方法主要设计用于监视系统状态
这个方法和上面的方法功能基本上差不多,一个传入thread对象一个是针对所有的线程。
ReentrantLock.hasWaiters(Condition condition)
hasWaiters(Condition condition):查询是否有任何线程正在等待与此锁关联的给定条件。请注意,由于超时和中断随时可能发生,因此返回 true并不能保证将来的signal会唤醒任何线程。此方法主要设计用于监视系统状态。
源码:
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
//sync调用的方法 AbstractQueuedSynchronizer.hasWaiters
public final boolean hasWaiters(ConditionObject condition) {
if (!owns(condition))
throw new IllegalArgumentException("Not owner");
return condition.hasWaiters();
}
验证:
public class ReentrantLockAPIMain5 {
public static ReentrantLock reentrantLock = new ReentrantLock();
public static Condition condition = reentrantLock.newCondition();
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
testFun1();
}
};
List<Thread> threadList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(runnable, "thread0" + i));
}
threadList.stream().forEach(thread -> thread.start());
Thread.sleep(5000);
testFun2();
}
public static void testFun1() {
reentrantLock.lock();
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public static void testFun2() {
reentrantLock.lock();
try {
System.out.println("has wait condition? " + reentrantLock.hasWaiters(condition) + " thread numbers is " + reentrantLock.getWaitQueueLength(condition));
condition.signal();
} finally {
reentrantLock.unlock();
}
}
}
运行结果:
等待同一个condition条件的线程正在等待中的数为10,返回为true
ReentrantLock.isLocked()
isLocked():查询此锁定是否由任意线程保持
源码:
final boolean isLocked() {
return getState() != 0;
}
ReentrantLock.isHeldByCurrentThread()
isHeldByCurrentThread():查询此锁是否由当前线程持有。类似于内置监视器锁的ThreadholdsLock(Object)方法,该方法通常用于调试和测试。
源码:
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
//sync调用方法
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
ReentrantLock.lockInterruptibly()
lockInterruptibly():除非当前线程为Threadinterrupt interrupted,否则获取锁。如果没有其他线程持有该锁,则获取该锁并立即返回,将锁保持计数设置为1.如果当前线程已经持有此锁,则持有计数将增加一,并且该方法将立即返回。如果该锁由另一个线程持有,则出于线程调度的目的,当前线程将被禁用,并处于休眠状态,直到发生以下两种情况之一:
1.该锁由当前线程获取
2.当前线程还有其他一些线程Threadinterrupt interrupts
源码:
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//sync调用的方法 AbstractQueuedSynchronizer.acquireInterruptibly
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
验证:
public class ReentrantLockAPIMain6 {
public static ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
testFun1();
}
};
Thread thread = new Thread(runnable, "thread01");
thread.start();
Thread.sleep(1000);
Thread thread1 = new Thread(runnable, "thread02");
thread1.start();
thread1.interrupt();
}
public static void testFun1() {
reentrantLock.lock();
try {
System.out.println("current thread name:" + Thread.currentThread().getName() + " get lock");
for (int i = 0; i < 6000; i++) {
Math.random();
}
System.out.println("get lock end " + Thread.currentThread().getName());
} finally {
if (reentrantLock.isHeldByCurrentThread()) {
reentrantLock.unlock();
}
}
}
}
运行结果:
可以运行成功,我们使用lockInterruptibly方法试试:我们将lock方法变为lockInterruptibly方法
reentrantLock.lockInterruptibly();
运行会出现报错信息:
ReentrantLock.tryLock()和ReentrantLock.tryLock(long time,TimeUnit unit)
tryLock():仅在调用时另一个线程未持有该锁时才获取该锁。如果没有其他线程持有该锁,则获取该锁,并立即返回值true,并将锁保持计数设置为1。即使已将此锁设置为使用公平的排序策略,如果可用,则对tryLock()的调用将立即获取该锁,无论当前是否有其他线程在等待该锁。锁。即使破坏公平性,这种“讨价还价”的行为在某些情况下还是有用的。如果要遵守此锁的公平性设置,请使用几乎等效的tryLock(long,TimeUnit)方法,tryLock(0,TimeUnit.SECONDS)(它还会检测到中断)。如果当前线程已经持有此锁,则持有计数将增加1,并且该方法返回true.如果锁由另一个线程持有,则此方法将立即返回值false