在讲重入锁(ReentrantLock)之前,相信大家都synchronized很熟悉了,它也是同步控制的一个重要锁,决定了一个线程是否可以访问临界资源,同时synchronized配合Object.wait()和Object.notify()的配合使用起到了等待通知的作用。这里如果大家不是很熟悉,可以查阅资料熟悉一下synchronized的使用。那么有synchronized这个锁,为什么还要介绍ReentrantLock,这肯定是有原因,接下来我就会介绍ReentrantLock比synchronized锁的优点。再说到优点之前,我们先看看ReentrantLock的基本使用。
ReenrantLock的基本使用
public class ReenterLock implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
public static int i =0;
@Override
public void run() {
for(int j = 0;j<10000;j++){
lock.lock();
try {
i++;
}
finally{
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
ReenterLock t = new ReenterLock();
Thread t1 =new Thread(t);
Thread t2 =new Thread(t);
t1.start();t2.start();
t1.join();t2.join();
System.out.println(i);
}
lock.lock();
lock.lock();
try{
i++;
}finally{
lock.unlock();
lock.unlock();
}
ReetrantLock的高级功能
- 可以中断响应
对于synchronized来说,如果一个线程在等待锁,那么结果只有两种情况,要么它获得锁继续执行,要么它就保持等待。而重入锁提供了另外一种可能,那就是线程可以中断。也就是在等待锁的过程,程序可以根据需要取消对锁的等待,看如下代码:
public class ReenterLock implements Runnable{
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lock;
public ReenterLock(int lock){
this.lock = lock;
}
@Override
public void run() {
try{
if (lock == 1){
lock1.lockInterruptibly();
try{
Thread.sleep(500);
}catch(InterruptedException e){}
lock2.lockInterruptibly();}
else{
lock2.lockInterruptibly();
try{
Thread.sleep(500);
}catch(InterruptedException e){}
lock1.lockInterruptibly();
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
if(lock1.isHeldByCurrentThread())
lock1.unlock();
if(lock2.isHeldByCurrentThread())
lock2.unlock();
System.out.println(Thread.currentThread().getId()+":线程退出");
}
}
public static void main(String[] args) throws InterruptedException {
ReenterLock r1 = new ReenterLock(1);
ReenterLock r2 = new ReenterLock(2);
Thread t1 =new Thread(r1);
Thread t2 =new Thread(r2);
t1.start();t2.start();
Thread.sleep(1000);
t2.interrupt();
}
}
再看看结果:
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:896)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
at demo.ReenterLock.run(ReenterLock.java:29)
at java.lang.Thread.run(Thread.java:745)
10:线程退出
9:线程退出
- 锁等待限时
除了等待外部通知之外,要避免死锁还有一种方法,那就是限时等待。举个例子,你等待朋友一起打球,在等待1小时,如果朋友迟迟不来,又无法联系到他,那么我们应该扫兴离去。对线程来说也是这样的,我们给一个线程等待锁的时间,如果在这个时间等不到锁,那么线程就会放弃。
public class ReenterLock implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try{
if(lock.tryLock(5, TimeUnit.SECONDS))
Thread.sleep(6000);
else{
System.out.println("get this lock fialed");
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
if(lock.isHeldByCurrentThread())
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReenterLock t = new ReenterLock();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();t2.start();
}
}
- 公平锁
在大多数情况下,锁的申请都是非公平的。也就是说,线程1首先请求了锁A,接着线程2页请求了锁A。那么当锁A可用时,是线程1可以获得锁还是线程2可以获得锁?在synchronized这个是不一定,系统会从这个锁的等待队列中随机挑选一个。不管谁先谁后,其实这就是不公平的。而公平锁,则不是这样,它会按照时间的先后顺序,保证先到者先得。这样就不会产生饥饿现象。虽然看起来公平锁很好,但是公平锁需要维护一个有序队列,所以性能就会下降,因此默认情况下,ReentrantLock是非公平的。下面看一段代码体验一下公平锁:
public class ReenterLock implements Runnable{
public static ReentrantLock fairlock = new ReentrantLock(true);//设置为true开启公平锁,默认是false
@Override
public void run() {
while(true){
try{
fairlock.lock();
System.out.println(Thread.currentThread().getName()+"获得锁");
}finally{
fairlock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
ReenterLock t = new ReenterLock();
Thread t1 = new Thread(t,"Thread_1");
Thread t2 = new Thread(t,"Thread_2");
t1.start();t2.start();
}
}
Thread_1获得锁
Thread_2获得锁
Thread_1获得锁
Thread_2获得锁
Thread_1获得锁
Thread_2获得锁
Thread_1获得锁
Thread_2获得锁
Thread_1获得锁
由于代码有大量的输出,这里只截取部分进行说明。这个输出结果中,很明显可以看到,两个线程是交替执行的。
Condition条件
public class ReenterLock implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();
@Override
public void run() {
try{
lock.lock();
condition.await();
System.out.println("Thread is going on");
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReenterLock t = new ReenterLock();
Thread t1 = new Thread(t);
t1.start();
Thread.sleep(2000);
//通知线程t1继续执行
lock.lock();
condition.signal();
lock.unlock();
}
}