ReentrantLock
-
在JDK5.0版本之前,重入锁的性能远远好于synchronized关键字,JDK6.0版本之后synchronized 得到了大量的优化,二者性能也不分伯仲,但是重入锁是可以完全替代synchronized关键字的。
-
ReentrantLock在扩展功能上更加强大,比如具有嗅探锁定、多路分支通知等,并且在使用上也比synchronized灵活(另外可以结合Condition来使用)。
-
ReentrantLock支持两种获取锁的方式,一种是公平模型,一种是非公平模型。
ReentrantLock使用例子:
//基本操作
public class ReentrantLockTest implements Runnable{
private static ReentrantLock lock=new ReentrantLock();
private static int count=1;
@Override
public void run() {
for(int i=0;i<100;i++){
lock.lock();
count++;
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockTest test1=new ReentrantLockTest();
ReentrantLockTest test2=new ReentrantLockTest();
Thread thread1=new Thread(test1);
Thread thread2=new Thread(test2);
thread1.start();
thread2.start();
try {
//主线程等待thread1和thread2结束后再执行下去
thread1.join();
thread2.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("count:"+count);
}
}
中断响应
lock 与 lockInterruptibly比较区别在于:
-
lock 优先考虑获取锁,待获取锁成功后,才响应中断。
-
lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取。
对于synchronized块来说,要么获取到锁执行,要么持续等待,interrupt()方法只是单纯地修改由于interrupt值,解决不了死锁问题。而重入锁的中断响应功能就合理地避免了这样的情况。比如,一个正在等待获取锁的线程被“告知”无须继续等待下去,就可以停止工作了。
synchronized例子(不能解决死锁):
//DeadLock
public class KillDeadlockSyn{
public static void main(String[] args){
Service service=new Service();
ThreadA threadA=new ThreadA(service);
ThreadB threadB=new ThreadB(service);
threadA.start();
threadB.start();
try {
Thread.sleep(10000);
threadA.interrupt();//是无法中断的,需要在线程中isInterrupted()判断再做处理
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Service {
Object LockA=new Object();
Object LockB=new Object();
public Service() {
}
public void methodA(){
synchronized (LockA){
System.out.println("methodA----LockA-------begin");
try {
Thread.sleep(3000);
synchronized (LockB){
System.out.println("methodA----LockB------begin");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void methodB(){
synchronized (LockB){
System.out.println("methodB----LockB------begin");
try {
Thread.sleep(3000);
synchronized (LockA){
System.out.println("methodB----LockA------begin");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ThreadA extends Thread{
private Service service;
public ThreadA(Service service) {
this.service=service;
}
@Override
public void run() {
service.methodA();
}
}
class ThreadB extends Thread{
private Service service;
public ThreadB(Service service) {
this.service=service;
}
@Override
public void run() {
service.methodB();
}
}
ReentrantLock解决死锁例子:
/**
*
* @author Administrator
*对于synchronized块来说,要么获取到锁执行,要么持续等待。而重入锁的中断响应功能就合理地避免了这样的情况。
*比如,一个正在等待获取锁的线程被“告知”无须继续等待下去,就可以停止工作了。
*/
public class KillDeadlock implements Runnable{
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lock;
public KillDeadlock(int lock) {
this.lock = lock;
}
@Override
public void run() {
try {
if (lock == 1) {
lock1.lockInterruptibly(); // 以可以响应中断的方式加锁
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
lock2.lockInterruptibly();
System.out.println("抢lock2成功");
int count=1;
while(true){
System.out.println(count++);
}
} else {
lock2.lockInterruptibly(); // 以可以响应中断的方式加锁
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
lock1.lockInterruptibly();
System.out.println("抢lock1成功");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock1.isHeldByCurrentThread()) lock1.unlock(); // 注意判断方式
if (lock2.isHeldByCurrentThread()) lock2.unlock();
System.err.println(Thread.currentThread().getId() + "退出!");
}
}
public static void main(String[] args) throws InterruptedException {
KillDeadlock deadLock1 = new KillDeadlock(1);
KillDeadlock deadLock2 = new KillDeadlock(2);
Thread t1 = new Thread(deadLock1);
Thread t2 = new Thread(deadLock2);
t1.start();t2.start();
Thread.sleep(1000);
t2.interrupt();
}
}
锁申请等待限时
ReentrantLock可以通过使用tryLock()或者tryLock(long timeout, TimeUtil unit) 来限制等待锁的时间
前者不带参数,这时线程尝试获取锁,如果获取到锁则继续执行,如果锁被其他线程持有,则立即返回 false ,也就是不会使当前线程等待,所以不会产生死锁。
后者带有参数,表示在指定时长内获取到锁则继续执行,如果等待指定时长后还没有获取到锁则返回false。
例子:
public class TryLockTest implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
while(!lock.tryLock(1, TimeUnit.SECONDS)){//一直尝试获取锁
}
System.out.println(Thread.currentThread().getName() + "获取锁成功!");
Thread.sleep(2000);
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
TryLockTest test = new TryLockTest();
Thread t1 = new Thread(test); t1.setName("线程1");
Thread t2 = new Thread(test); t2.setName("线程2");
t1.start();
Thread.sleep(500);
t2.start();
}
}
结果:
线程1获取锁成功!
线程2获取锁成功!
首先启动线程1,线程1 尝试去获取锁,获取到锁,睡眠2秒,线程2在线程1启动后0.5秒启动,在while语句中一直尝试获取锁,等线程1结束后,线程2获取到锁。
公平锁与非公平锁
ReentrantLock中有两种锁,分别为"公平锁"和"非公平锁"。
公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。
非公平锁:是一种获取锁的抢占机制,随机获取锁,和公平锁不一样的是先来的不一定先拿到锁,这种方式有可能导致某些线程会一直拿不到锁。
源码:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
例子:
public class FairLockTest implements Runnable{
//true则定义为公平锁,false则定义为非公平锁(默认也是非公平锁)
public static ReentrantLock lock = new ReentrantLock(true);
//public static ReentrantLock lock = new ReentrantLock(false);
@Override
public void run() {
while (true) {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获取到了锁!");
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
FairLockTest test = new FairLockTest();
Thread t1 = new Thread(test, "线程1");
Thread t2 = new Thread(test, "线程2");
t1.start();
t2.start();
}
}
ReentrantLock 配合 Conditond 使用
配合关键字synchronized使用的方法如:await()、notify()、notifyAll(),同样配合ReentrantLock 使用的Conditon提供了以下方法:
public interface Condition {
void await() throws InterruptedException; // 类似于Object.wait()
void awaitUninterruptibly(); // 与await()相同,但不会再等待过程中响应中断
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal(); // 类似于Obejct.notify()
void signalAll();
}
例子:
public class ReentrantLockWithConditon {
public static void main(String[] args) throws InterruptedException {
MyService myService=new MyService();
ThreadAwait threadAwait=new ThreadAwait(myService);
new Thread(threadAwait).start();
Thread.sleep(2000);
ThreadSingal threadSingal=new ThreadSingal(myService);
new Thread(threadSingal).start();
}
}
class MyService{
public static ReentrantLock lock = new ReentrantLock(true);
public static Condition condition = lock.newCondition();
public void await(){
lock.lock();
System.out.println("await:"+new Date());
try {
condition.await();
System.out.println("after await:"+new Date());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void signal(){
lock.lock();
System.out.println("signal:"+new Date());
try {
condition.signal();
System.out.println("after signal:"+new Date());
}finally{
lock.unlock();
}
}
}
class ThreadAwait implements Runnable{
private MyService myService;
public ThreadAwait(MyService myService) {
this.myService=myService;
}
@Override
public void run() {
myService.await();
}
}
class ThreadSingal implements Runnable{
private MyService myService;
public ThreadSingal(MyService myService) {
this.myService=myService;
}
@Override
public void run() {
myService.signal();
}
}