重入锁java.util.concurrent.locks.ReentrantLock(笔记)

一、重入锁

重入锁可以完全代替synchronized关键字.在JDK5.0的早期版本中,重入锁的性能远远好于synchronized,但是从JDK6.0开始.JDK在synchronized上做了大量的优化.使得两者的性能差距不大·


重入锁对于逻辑控制的灵活性要远远好于synchronized,但是要必须释放锁,否则 其他线程就会一直等待!另外重入锁允许一个线程连续几次获取同一把锁,但是也要释放相同次数。当然如果释放次数多了,也会报错java.lang.IllegalMonitorStateException


二、中断响应

对于synchronized而言,如果一个线程在等待锁,那么结果只要两种情况: 获得这把锁执行,或者保持等待状态。 而使用重入锁,则提供了另外一种可能性,那就是线程可以被中断,也就是在等待过程中,程序可以根据需要取消对锁的请求.

2.1 案例分析

下面代码产生了一个死锁,但是得益于锁的中断,我们可以很轻松的解决死锁

package myThread;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程t1和t2启动后,t1占用lock1 再占用lock2  t2先占用lock2 然后请求lock1 因此很容易形成互相等待,
 * 当我们让t2中断时,他放弃了申请lock1 然后释放了lock2 实际上是 t1线程 完成任务正常退出,而t2 是中断的
 */
public class IntLock implements Runnable {
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();
    int lock;

    public IntLock(int lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            if (lock == 1) {
                lock1.lockInterruptibly();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock2.lockInterruptibly();
            } else {
                lock2.lockInterruptibly();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                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 {
        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.interrupt();//中断其中一个线程

    }
}

运行结果:

12:线程退出
11:线程退出
java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
    at myThread.IntLock.run(IntLock.java:36)
    at java.lang.Thread.run(Thread.java:745)

三、锁申请等待限时

锁申请等待限时 除了等待通知之外,要避免死锁还有另外一种方法,那就是限时等待, 就是规定一个时间,超出时间没有拿到锁 就退出

package myThread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TimeLock implements Runnable {
    public static ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            if (lock.tryLock(5, TimeUnit.SECONDS)) {//试图获取锁,等待5秒 如果超时那就false
                Thread.sleep(6000);
            } else {
                System.out.println("get lock failed");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        TimeLock r1 = new TimeLock();
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r1);
        t1.start();
        t2.start();
    }
}

运行结果

get lock failed


由于占用锁的线程会持有锁长达6s,故另一个线程无法在5s的等待时间内获得锁,因此,请求锁会失败!


ReentrantLock.tryLock()方法可以不带参数直接运行,在这种情况下,当前线程会尝试获得锁,如果锁并未被其他线程占用,则申请会成功!并立即返回true,如果锁被其他线程占用,则当前线程不会执行等待,而是立即返回false。


参考

  1. 《实战java高并发程序设计》

猜你喜欢

转载自blog.csdn.net/qq_31156277/article/details/80658310