首页 > 代码库 > java concurrent之ReentrantLock
java concurrent之ReentrantLock
在编码的过程中,有时候我们不得不借助锁同步来保证线程安全。synchronized关键字在上一篇博客中已经介绍;自从JDK5开始,添加了另一种锁机制:ReentrantLock。
二者的区别
1、lock是jdk5之后代码层面实现的,synchronized是JVM层面实现的。
2、synchronized在出现异常的时候能够自动释放锁,而lock必须在finally块中unlock()主动释放锁,否则会死锁。
3、在竞争不激烈的时候synchronized的性能是比lock好一点的,但是当竞争很激烈时synchronized的性能会相对几十倍的下降,因为
lock用了新的锁机制,新的Lock机制最终归结到一个原子性操作上。
4、synchronized无法中断一个正在等候获得锁的线程,也无法通过投票得到锁,如果不想等下去,也就没法得到锁;而lock可以。
5、ReentrantLock可以采用FIFO的策略进行竞争,更加公平。
基本用法
先写个简单的例子看一下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestReentrantLock {
public static void main(String[] args) {
final ReentrantLock rLock = new ReentrantLock();
final Condition condition = rLock.newCondition();
ExecutorService executorService = Executors.newFixedThreadPool(5);
Runnable opt = new Runnable() {
@Override
public void run() {
rLock.lock();
System.out.println(Thread.currentThread().getName()+"--->>lock()");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName()+"--->>unlock()");
rLock.unlock();
}
}
};
for (int i = 0; i < 4; i++) {
executorService.submit(opt);
}
Runnable release = new Runnable() {
@Override
public void run() {
rLock.lock();
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+"--->>signalAll()");
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rLock.unlock();
}
}
};
executorService.submit(release);
executorService.shutdown();
}
}
运行结果:
pool-1-thread-1--->>lock()
pool-1-thread-2--->>lock()
pool-1-thread-3--->>lock()
pool-1-thread-4--->>lock()
pool-1-thread-5--->>signalAll()
pool-1-thread-1--->>unlock()
pool-1-thread-2--->>unlock()
pool-1-thread-3--->>unlock()
pool-1-thread-4--->>unlock()
上面代码中有个Condition,它的三个方法await 、 signal 和 signalAll,与基类的wait、notify和notifyAll方法相对应,因为它们不能覆盖Object上的对应方法,所以就起了这三个奇葩的名字。由上面的代码可以看出ReentrantLock和synchronized用法是基本相同的。
中断ReentrantLock
实例如下:
package co.etc.concurrent;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockSample {
public static void main(String[] args) {
testReentrantLock();
}
public static void testReentrantLock() {
final SampleSupportLock support = new SampleSupportLock();
Thread first = new Thread(new Runnable() {
public void run() {
try {
support.doSomething();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread second = new Thread(new Runnable() {
public void run() {
try {
support.doSomething();
} catch (InterruptedException e) {
System.out.println("InterruptedException--->>");
}
}
});
executeTest(first, second);
}
public static void executeTest(Thread a, Thread b) {
a.start();
try {
Thread.sleep(100);
b.start();
Thread.sleep(1000);
System.out.println("---->>>interrupt()");
b.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
abstract class SampleSupport {
protected int counter;
public void startTheCountdown() {
long currentTime = System.currentTimeMillis();
for (;;) {
long diff = System.currentTimeMillis() - currentTime;
if (diff > 2000) {
break;
}
}
}
}
class SampleSupportLock extends SampleSupport {
private final ReentrantLock lock = new ReentrantLock();
public void doSomething() throws InterruptedException {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName()
+ "doSomething()--->>");
startTheCountdown();
try {
counter++;
} finally {
lock.unlock();
}
System.out.println("counter---->>>"+counter);
}
}
运行结果:
Thread-0doSomething()--->>
---->>>interrupt()
InterruptedException--->>
counter---->>>1
运行结果表明第二个线程被中断了,这是因为我用的是lock.lockInterruptibly();在主线程中我调用了b.interrupt();二synchronized是没法做到的
公平性
实例代码如下:
import java.util.Collection;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestFairLock {
private static Lock fairLock = new ReentrantLock2(true);
private static Lock unfairLock = new ReentrantLock2();
public static void main(String[] args) {
TestFairLock testFairLock = new TestFairLock();
// testFairLock.unfair();
testFairLock.fair();
}
public void fair() {
System.out.println("fair version");
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Job(fairLock)) {
public String toString() {
return getName();
}
};
thread.setName("" + i);
thread.start();
}
// sleep 5000ms
}
public void unfair() {
System.out.println("unfair version");
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Job(unfairLock)) {
public String toString() {
return getName();
}
};
thread.setName("" + i);
thread.start();
}
// sleep 5000ms
}
private static class Job implements Runnable {
private Lock lock;
public Job(Lock lock) {
this.lock = lock;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
lock.lock();
try {
System.out.println("Thread--->>"
+ Thread.currentThread().getName());
} finally {
lock.unlock();
}
}
}
}
private static class ReentrantLock2 extends ReentrantLock {
private static final long serialVersionUID = 1773716895097002072L;
public ReentrantLock2(boolean b) {
super(b);
}
public ReentrantLock2() {
super();
}
public Collection<Thread> getQueuedThreads() {
return super.getQueuedThreads();
}
}
}
运行结果
unfair version
Thread--->>0
Thread--->>0
Thread--->>0
Thread--->>0
Thread--->>0
Thread--->>1
Thread--->>1
Thread--->>1
Thread--->>1
Thread--->>1
Thread--->>2
Thread--->>2
Thread--->>2
Thread--->>2
Thread--->>2
Thread--->>3
Thread--->>3
Thread--->>3
Thread--->>3
Thread--->>3
Thread--->>4
Thread--->>4
Thread--->>4
Thread--->>4
Thread--->>4
fair version
Thread--->>0
Thread--->>0
Thread--->>1
Thread--->>3
Thread--->>0
Thread--->>4
Thread--->>2
Thread--->>1
Thread--->>3
Thread--->>0
Thread--->>4
Thread--->>2
Thread--->>1
Thread--->>3
Thread--->>0
Thread--->>4
Thread--->>2
Thread--->>1
Thread--->>3
Thread--->>4
Thread--->>2
Thread--->>1
Thread--->>3
Thread--->>4
Thread--->>2
从运行结果看到用ReentrantLock(boolean fair)构建的锁,相对ReentrantLock()是更公平的,当fair为true时采用的是FIFO策略,所
以各个线程能够更平均的分配时间。