并发包下的重入锁(ReentrantLock)

重入锁可以完全替代synchronized关键字。在JDK5.0的早期版本中,重入锁的性能完全好于synchronized。但是JDK6.0对synchronized做了大量的优化,使得两者差距并不大了,但是 ReentrantLock 灵活性要远高于synchronized。

1.为什么叫重入锁?

答:从名称上看,翻译挺贴切的, re-entrant-lock 重-入-锁。之所以这么叫是因为这种锁是可以反复进入的。当然,仅仅局限于一个线程是可以反复的。

lock.lock();

lock.lock();

try {

i++;

}catch (Exception e) {

lock.unlock();

lock.unlock();

}

一个线程可以连续两次获得同一把锁。释放锁也要多释放一次。

2.中断响应

对于synchronized来说,如果一个线程在等待锁,那么只有与两种结果,第一,得到锁,第二 继续等待。而使用重入锁,则提供另外一种可能,那就是线程可以被中断。也就是程序可以选择性的取消对锁的请求,在有些时候还是有必要的。

举个例子:如果你约好了和朋友一起去玩,结果等了1个小时,你朋友给你打电话说有突发情况不能去了。那么你一定扫兴的打道回府了。中断就类似于这么一套机制。如果一个线程正在等待锁,那么它依然可以收到一个通知,被告知无须再等待,可以停止工作了。这种情况对处理死锁是有一定帮助的。

下面的代码产生了一个死锁,我们通过重入锁的中断,可以有效地解决这个问题。

package com.example.test;

import java.util.concurrent.locks.ReentrantLock;

/**

* Created by sql_j on 2018/3/18.

*/

public class IntLockimplements Runnable {

public static ReentrantLocklock1 =new ReentrantLock();

public static ReentrantLocklock2 =new ReentrantLock();

int lock;

public IntLock(int lock){

this.lock = lock;

}

@Override

    public void run() {

try {

if(lock==1){

lock1.lockInterruptibly();

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

e.printStackTrace();

}

lock2.lockInterruptibly();

System.out.println("我是线程1");

}else{

lock2.lockInterruptibly();

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

e.printStackTrace();

}

lock1.lockInterruptibly();

System.out.println("我是线程2");

}

}catch (Exception e) {

e.printStackTrace();

}finally {

if(lock1.isHeldByCurrentThread()){

lock1.unlock();

}

if(lock2.isHeldByCurrentThread()){

lock2.unlock();

}

}

}

public static void main(String[] args){

IntLock intLock1 =new IntLock(1);

IntLock intLock2 =new IntLock(2);

Thread thread1 =new Thread(intLock1);

Thread thread2 =new Thread(intLock2);

thread1.start();

thread2.start();

try {

Thread.sleep(1000);

}catch (InterruptedException e) {

e.printStackTrace();

}

thread1.interrupt();

}

}

线程thread1和线程thread2启动后, thread1先占用lock1 再占用lock2;thread2先占用lock2 再占用lock1,很容易形成 两个线程相互等待。这里用lickInterruptibly()方法。这个方法可以在等待锁的过程中,响应中断。

 

 

 

3.trylock()

我们可以用trylock()方法进行限时等待。

 

 

public class IntLockimplements Runnable {

public static ReentrantLocklock1 =new ReentrantLock();

@Override

    public void run() {

try {

try {

if(lock1.tryLock(3, TimeUnit.SECONDS)){

Thread.sleep(6000);

}else{

System.out.println("get lock feild");

}

}catch (InterruptedException e) {

e.printStackTrace();

}

}catch (Exception e) {

e.printStackTrace();

}finally {

if(lock1.isHeldByCurrentThread()){

lock1.unlock();

}

}

}

}

 

我们同样开启两个线程,这时候 trylock(),会在3S内试着去请求锁,请求不到 就会返回false。

 

ReentrantLock.tryLock();方法也可以不带参数直接运行。

 

 

3.公平锁

我们在平时的应用中就知道,在大多数情况下,锁的申请是非公平的,无序的。这就好比,买票不要排队,谁能挤到最前面,谁就先买到票。而公平锁,是按照时间顺序来排队的,它不会产生饥饿现象,只要你排队,就一定能得到资源。如果我们使用synchronized关键字进行锁的控制,那它就是非公平的。

 

示例:

 

public static ReentrantLock fireLock = new ReentrantLock(true);

    @Override

    public void run() {

        try {

            try {

                if(fireLock.tryLock(3, TimeUnit.SECONDS)){

                }else{

                    System.out.println("get lock feild");

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            if(fireLock.isHeldByCurrentThread()){

                fireLock.unlock();

            }

        }

    }

 

 

 

4.重入锁的好搭档:Condition条件

Condition和 Object.wait()、Obejct.notify() 方法的作用是大致相同的。只不过,Condition是作用在ReentrantLock上,而Object.wait()、Obejct.notify() 是作用在 synchronized上的。

await():方法会使当前线程等待,同时释放当前锁,其它线程中使用signal()或signalAll()方法时,线程会重新获得锁,并继续执行。或者当线程被中断时,也能跳出等待。

awaitUninteerrupibly():和await()方法类似,但是不会在等待响应过程中被中断。

signal():用于唤醒一个在等待中的线程。

signalAll():用于唤醒所有在等待的线程。

示例:

package com.example.test;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.ReentrantLock;

/**

* Created by sql_j on 2018/3/18.

*/

public class IntLockimplements Runnable {

private static ReentrantLocklock =new ReentrantLock(true);

private static Conditioncondition =lock.newCondition();

@Override

    public void run() {

try {

lock.lock();

condition.await();

System.out.println("执行");

}catch (InterruptedException e) {

e.printStackTrace();

}finally {

if(lock.isHeldByCurrentThread()){

lock.unlock();

}

}

}

public static void main(String[] args){

IntLock intLock =new IntLock();

Thread thread =new Thread(intLock);

thread.start();

try {

Thread.sleep(3000);

}catch (InterruptedException e) {

e.printStackTrace();

}

lock.lock();

condition.signal();

lock.unlock();

}

}

 

5.对于ReentrantLock的应用整理如下

    lock():获得锁,如果锁被占用,则等待。

    lockInterruptibly():获得锁,但优先响应中断。

    tryLock():尝试获得锁,如果成功,返回true,失败返回false。该方法不等待,立即返回。

    tryLock(long time,TimeUnit unit):在给定时间内尝试获得锁。

    unlock():释放锁。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值