重入锁ReentrantLock

来源:互联网 发布:知乎关于网络文章 编辑:程序博客网 时间:2024/05/01 07:00
        线程的同步控制,最基础的就是synchronized关键字。但是在使用的过程中有一些限制,并没有那么的灵活。
可重入

        所以下面介绍一下ReentrantLock的使用和相关特性,一个简单的demo如下:

public class ReenterLock implements Runnable {public static ReentrantLock lock = new ReentrantLock();public static int i = 0;@Overridepublic void run() {for (int j = 0; j < 10000000; j++) {lock.lock();try {i++;} finally {lock.unlock();}}}public static void main(String[] args) throws InterruptedException {ReenterLock r1 = new ReenterLock();Thread t1 = new Thread(r1);Thread t2 = new Thread(r1);t1.start();t2.start();t1.join();t2.join();System.out.println(i);}}

        上面的demo创建了两个线程 t1、t2,并且同时使用了lock作为锁,完成了i++的操作。这里面需要注意的是,lock.lock()调用了几次,响应的lock.unlock()就应该调用几次。并且无论执行体如何,最终都要调用lock.unlock()。
响应中断
        在两个线程同时抢占同一个锁资源的时候,很有可能产生死锁的现象。ReentrantLock可以支持中断响应,从而规避死锁的产生。

public class IntLock implements Runnable {public static ReentrantLock lock1 = new ReentrantLock();public static ReentrantLock lock2 = new ReentrantLock();int lock;/** * 控制加锁顺序,方便构造死锁 *  * @param lock */public IntLock(int lock) {this.lock = lock;}@Overridepublic void run() {try {if (lock == 1) {lock1.lockInterruptibly();try {Thread.sleep(500);} catch (Exception e) {}lock2.lockInterruptibly();} else {lock2.lockInterruptibly();try {Thread.sleep(500);} catch (Exception e) {}lock1.lockInterruptibly();}} catch (Exception 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 {IntLock r1 = new IntLock(1);IntLock r2 = new IntLock(2);Thread t1 = new Thread(r1);Thread t2 = new Thread(r2);t1.start();t2.start();Thread.sleep(1000);// 中断t2线程t2.interrupt();}}
        从上面代码可以看出来,此时使用的是lock.lockInterruptibly(),而不是lock.lock()。当使用lock.lockInterruptibly()加锁等待的时候,可以响应中断,从而使线程对出死锁状态。
        上面代码一共起了两个线程,故意造成了死锁的状态。此时在主线程中调用了中断方法,此时t2线程对出等待,释放锁资源,从而可以让t1继续执行下去。
超时限制
        当多线程对同一监视器对象加锁时,如果超过一定时间还没获得锁,则立即返回false。这里使用到的是tryLock()。
public class TimeLock implements Runnable {public static ReentrantLock lock = new ReentrantLock();@Overridepublic void run() {try {if (lock.tryLock(5, TimeUnit.SECONDS)) {Thread.sleep(6000);} else {System.out.println("I got lock failed.");}} catch (Exception e) {e.printStackTrace();} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}}}public static void main(String[] args) {TimeLock time1 = new TimeLock();Thread t1 = new Thread(time1);Thread t2 = new Thread(time1);t1.start();t2.start();}}
        上述代码启动了2个线程,在5S内必定一个线程获取锁对象失败。
        另外tryLock()不带参的方法,表示对当前对象加锁,如果获取不到则立即返回false。通过这个特性,可以在程序中用无限循环,使其不断的去尝试加锁。
public class TryLock implements Runnable {public static ReentrantLock lock1 = new ReentrantLock();public static ReentrantLock lock2 = new ReentrantLock();int lock;public TryLock(int lock) {this.lock = lock;}@Overridepublic void run() {if (lock == 1) {while (true) {if (lock1.tryLock()) {try {try {Thread.sleep(500);} catch (Exception e) {}if (lock2.tryLock()) {try {System.out.println(Thread.currentThread().getId() + ": my job done");return;} finally {lock2.unlock();}}} finally {lock1.unlock();}}}} else {while (true) {if (lock2.tryLock()) {try {try {Thread.sleep(500);} catch (Exception e) {}if (lock1.tryLock()) {try {System.out.println(Thread.currentThread().getId() + ": my job done");return;} finally {lock1.unlock();}}} finally {lock2.unlock();}}}}}public static void main(String[] args) {TryLock r1 = new TryLock(1);TryLock r2 = new TryLock(2);Thread t1 = new Thread(r1);Thread t2 = new Thread(r2);t1.start();t2.start();}}
公平锁
        在大部分的应用场景下,锁的申请获取都是不公平的。但是我们通过ReentrantLock可以构造出公平的锁对象。
public ReentrantLock(boolean fair)
        使用上述方法可以构造出公平锁出来,我们通常默认的fair都是false。公平锁本身会维护一个有序队列,开销相对较高。

public class FairLock implements Runnable {public static ReentrantLock fairLock = new ReentrantLock(true);@Overridepublic void run() {while (true) {try {fairLock.lock();System.out.println(Thread.currentThread().getName() + " 获得锁");} finally {fairLock.unlock();}}}public static void main(String[] args) {FairLock r1 = new FairLock();Thread t1 = new Thread(r1, "thread t1");Thread t2 = new Thread(r1, "Thread t2");t1.start();t2.start();}}
        通过运行上述程序可以看出,锁的获取都是有序的,公平的。
综上所述
        一般来说,根据JVM的调度,系统更倾向于把锁给当前已经获得锁的线程。从而减少线程频繁切换带来的系统消耗。
        对于ReentrantLock主要有以下几个方法:

  • lock():获取锁,如果已被占用,则等待;
  • lockInterruptibly():获得锁,但是响应中断;
  • tryLock():获得锁,如可以立即获得,则返回true,如不可以,则立即返回false;
  • tryLock(long time, TimeUnit unit):超时未获得锁,则返回;
  • unlock():释放锁;

链接:http://moguhu.com/article/detail?articleId=26

原创粉丝点击