ReentrantLock 可重入的锁是我们平常除了intrinsic lock (也就是 synchronized 方法, synchronized block)之外用得最多的了同步方式了。 一般情况下 我们用 ReentrantLock 的时候就是用它的默认建构函数方式
new ReentrantLock ();
但其实它带一个 参数 是否 fair。如果是true 也就是FairSync 所在有多个线程同时竞争这个锁得时候, 会考虑公平性尽可能的让不同的线程公平。 这个公平其实是有很大的性能损失换来的。下面有个例子 : \
package com.bwang.concurrent;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.CyclicBarrier;
import static java.lang.System.out;
public final class TestLocks implements Runnable
{
public enum LockType { JVM, JUC }
public static LockType lockType;
public static final long ITERATIONS = 5L * 1000L * 1000L;
public static long counter = 0L;
public static final Object jvmLock = new Object();
public static final Lock jucLock = new ReentrantLock(false);
private static int numThreads;
private final long iterationLimit;
private final CyclicBarrier barrier;
private long localCounter = 0L;
public long getLocalCounter()
{
return localCounter;
}
public TestLocks(final CyclicBarrier barrier, final long iterationLimit)
{
this.barrier = barrier;
this.iterationLimit = iterationLimit;
}
public static void main(final String[] args) throws Exception
{
lockType = LockType.valueOf("JUC");
numThreads = Integer.parseInt("8");
final long start = System.nanoTime();
runTest(numThreads, ITERATIONS);
final long duration = System.nanoTime() - start;
out.printf("%d threads, duration %,d (ns)\n", numThreads, duration);
out.printf("%,d ns/op\n", duration / ITERATIONS);
out.printf("%,d ops/s\n", (ITERATIONS * 1000000000L) / duration);
out.println("counter = " + counter);
}
private static void runTest(final int numThreads, final long iterationLimit)
throws Exception
{
CyclicBarrier barrier = new CyclicBarrier(numThreads);
Thread[] threads = new Thread[numThreads];
TestLocks[] testLocks = new TestLocks[numThreads];
for (int i = 0; i < threads.length; i++)
{
testLocks[i] = new TestLocks(barrier, iterationLimit);
threads[i] = new Thread(testLocks[i]);
}
for (Thread t : threads)
{
t.start();
}
for (Thread t : threads)
{
t.join();
}
for (int i = 0; i < threads.length; i++)
{
out.printf("%d thread, local counter = %,d\n", i, testLocks[i].getLocalCounter());
}
}
public void run()
{
try
{
barrier.await();
}
catch (Exception e)
{
// don't care
}
switch (lockType)
{
case JVM: jvmLockInc(); break;
case JUC: jucLockInc(); break;
}
}
private void jvmLockInc()
{
while (true)
{
long count = 0;
synchronized (jvmLock)
{
++counter;
count = counter;
}
localCounter++;
if (count >= iterationLimit) {
break;
}
}
}
private void jucLockInc()
{
while (true)
{
long count = 0L;
jucLock.lock();
try
{
++counter;
count = counter;
}
finally
{
jucLock.unlock();
}
localCounter++;
if (count >= iterationLimit) {
break;
}
}
}
}
我们简单用N个线程来同步一个counter 5,000,000次。 如果是 new ReentrantLock(true) 也就是 FairSync 方式 :
0 thread, local counter = 624,822
1 thread, local counter = 625,135
2 thread, local counter = 624,936
3 thread, local counter = 624,800
4 thread, local counter = 625,007
5 thread, local counter = 624,921
6 thread, local counter = 625,298
7 thread, local counter = 625,088
8 threads, duration 16,553,236,994 (ns)
3,310 ns/op
302,055 ops/s
counter = 5000007
可以看到8 个线程 每个线程的获取lock都很接近 但是它要 3310 个ns 来进行一次。 如果采用 如果是 new ReentrantLock(false) 就是 UnfairSync 方式:
0 thread, local counter = 626,786
1 thread, local counter = 594,983
2 thread, local counter = 590,274
3 thread, local counter = 688,725
4 thread, local counter = 588,090
5 thread, local counter = 586,885
6 thread, local counter = 732,210
7 thread, local counter = 592,054
8 threads, duration 425,844,254 (ns)
85 ns/op
11,741,381 ops/s
counter = 5000007
虽然 每个thread 获取lock 的次数差异很大 从 592,054到 732,210, 但每次操作自需要 85 ns。 3310 对 85 这个差异太大聊。
如果我们用intrinsic lock 的方法 结果如下:
0 thread, local counter = 498,363
1 thread, local counter = 512,603
2 thread, local counter = 799,367
3 thread, local counter = 500,946
4 thread, local counter = 824,935
5 thread, local counter = 652,921
6 thread, local counter = 692,219
7 thread, local counter = 518,653
8 threads, duration 877,777,848 (ns)
175 ns/op
5,696,202 ops/s
counter = 5000007
intrinsic lock 也应该是unfair 的方式, 每个线程获取的机会差异比较大, 每个操作需要 175ns。 比 unfair 的 ReentrantLock 性能差些。
得出的结果是 如果我们仅考虑同步锁得性能不需要考虑公平性优先考虑
new ReentrantLock(false)
再次是 intrinsic lock
万不得已的必须要FairSync 的情况下才用 new ReentrantLock(true)。
分享到:
相关推荐
java语言 并发编程 ReentrantLock与synchronized区别 详解
ReentrantLock与synchronized来的区别 1.synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。 2.synchronized...
1、ReentrantLock简介 2、ReentrantLock函数列表 3、重入的实现 4、公平锁与非公平锁 5、ReentrantLock 扩展的功能 6
助于理解的例子 博文链接:https://uule.iteye.com/blog/1488356
ReentrantLock的使用及注意事项
ReentrantLock源码剖析
一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个...
比较 Locks 框架与传统 synchronized 关键字的不同之处。 ReentrantLock 简介: 详细讲解 ReentrantLock 的概念和特点。解释为什么它被称为“可重入锁”,以及如何解决传统锁可能的问题。 ReentrantLock 的基本用法...
java lock synchronized
ReentrantLock 实现原理 1
ReentrantLock lock方法注释
1. ReentrantLock的介绍 ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java...
NULL 博文链接:https://patrick002.iteye.com/blog/2170391
近日,阅读jdk并发包源码分析整理笔记。
公平与非公平的一个很本质的区别就是,是否遵守FIFO(也就是先来后到)。当有多个线程来申请锁的时候,是否先申请的线程先获取锁,后申请的线程后获取锁?如果是的,则是 公平锁 ,否则是 非公平锁 。 更准确地...
ReentrantLock.java
Lock、Synchoronized和ReentrantLock的使用
可重入锁: 也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,...ReentrantLock 在Java也是一个基础的锁,ReentrantLock 实现Lock接口提供一系列的基础函数,开发人员可以灵活的是应用函数满足各种复杂多变应用场景;
Java多线程ReentrantLock1
AQS和ReentrantLock.pdf