JUC锁框架——重入锁ReentrantLock

重入锁(ReentrantLock)

  ReentrantLock是一种可重入的互斥锁,而且加锁是一种显式操做,对逻辑控制的灵活性远远大于synchronized关键字。重入锁是能够彻底替代synchronized。而且重入锁的性能是远高于synchronized的,可是jdk6.0开始,jdk对synchronized作了大量的优化,使得二者性能差距不大。另外,ReentrantLock可结合Condition、以及提供了中断响应、锁申请等待限时、公平锁等。java

公平锁、非公平锁(ReentrantLock)

  ReentrantLock分为“公平锁”和“非公平锁”。它们的区别体如今获取锁的机制上是否公平。在“公平锁”的机制下,线程依次排队获取锁;而“非公平锁”在锁是可获取状态时,无论本身是否是在队列的开头都会获取锁。函数

 运行以下代码,对比公平锁和非公平锁的运行结果性能

public static void main(String[] args) throws InterruptedException {
        Lock noFairReentrantLock = new ReentrantLock();
        Lock fairReentrantLock = new ReentrantLock(true);

        Thread[] threads = new Thread[10];
        for(int i=0;i<10;i++){
            threads[i] = new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"  [start]");
                fairReentrantLock.lock();//也能够切换为非公平锁,观察运行结果
                try {
                    System.out.println(Thread.currentThread().getName()+"  [得到锁]");
                }finally {
                    fairReentrantLock.unlock();
                }
            });
        }
        for(int i=0;i<5;i++){
            threads[i].start();
        }
        for(int i=0;i<5;i++){
            threads[i].join();
        }
    }

}

重入锁的中断响应功能

对于synchronized块来讲,要么获取到锁执行,要么持续等待。而重入锁的中断响应功能就合理地避免了这样的状况。好比,一个正在等待获取锁的线程被“告知”无须继续等待下去,就能够中止工做了。优化

下面咱们看一个利用中断响应功能解决的一个死锁问题线程

public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock1 = new ReentrantLock();
        ReentrantLock lock2 = new ReentrantLock();

        //线程t1和t2构成了死锁,此时咱们能够中断的方式解决死锁问题
        Runnable runnable = ()->{
            try {
                if(Thread.currentThread().getName().equals("t1")){
                    lock1.lockInterruptibly(); //在加锁的过程当中仍然能够相应中断
                    Thread.sleep(100);
                    lock2.lockInterruptibly();
                }else{
                    lock2.lockInterruptibly();
                    Thread.sleep(100);
                    lock1.lockInterruptibly();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                if (lock1.isHeldByCurrentThread()) lock1.unlock();
                if (lock2.isHeldByCurrentThread()) lock2.unlock();
            }
        };

        Thread t1 = new Thread(runnable,"t1");
        Thread t2 = new Thread(runnable,"t2");

        t1.start();t2.start();
        Thread.sleep(1000);
        //以中断的方式解决死锁问题
        t2.interrupt();
    }

锁申请等待限时

可使用 tryLock()或者tryLock(long timeout, TimeUtil unit) 方法进行一次限时的锁等待。code

  • tryLock(),线程尝试获取锁,若是获取到锁则继续执行,若是锁被其余线程持有,则当即返回false,也就是不会使当前线程等待,因此不会产生死锁。
  • tryLock(long timeout, TimeUtil unit),表示在指定时长内获取到锁则继续执行,若是等待指定时长后尚未获取到锁则返回false。
public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Runnable runnable = ()->{
            try {
                if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待1秒
                    Thread.sleep(2000);
                } else {
                    System.err.println(Thread.currentThread().getName() + "获取锁失败!");
                }
            } catch (Exception e) {
                if (lock.isHeldByCurrentThread()) lock.unlock();
            }
        };
        Thread t1 = new Thread(runnable,"t1");
        Thread t2 = new Thread(runnable,"t2");
        t1.start();t2.start();
    }

ReentrantLock 配合 Condition 使用

配合关键字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();
}

ReentrantLock 配合 Condition的例子队列

public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock(true);
        Condition condition = lock.newCondition();
        Thread t = new Thread(()->{
            try {
                lock.lock();
                System.err.println(Thread.currentThread().getName() + "-线程开始等待...");
                condition.await();
                System.err.println(Thread.currentThread().getName() + "-线程继续进行了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t1");
        t.start();
        Thread.sleep(1000);
        System.err.println("过了1秒后...");
        lock.lock();
        condition.signal(); // 调用该方法前须要获取到建立该对象的锁不然会产生java.lang.IllegalMonitorStateException异常
        lock.unlock();
    }

ReentrantLock函数列表

// 建立一个 ReentrantLock ,默认是“非公平锁”。
ReentrantLock()
// 建立策略是fair的 ReentrantLock。fair为true表示是公平锁,fair为false表示是非公平锁。
ReentrantLock(boolean fair)
// 查询当前线程保持此锁的次数。
int getHoldCount()
// 返回目前拥有此锁的线程,若是此锁不被任何线程拥有,则返回 null。
protected Thread getOwner()
// 返回一个 collection,它包含可能正等待获取此锁的线程。
protected Collection<Thread> getQueuedThreads()
// 返回正等待获取此锁的线程估计数。
int getQueueLength()
// 返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程。
protected Collection<Thread> getWaitingThreads(Condition condition)
// 返回等待与此锁相关的给定条件的线程估计数。
int getWaitQueueLength(Condition condition)
// 查询给定线程是否正在等待获取此锁。
boolean hasQueuedThread(Thread thread)
// 查询是否有些线程正在等待获取此锁。
boolean hasQueuedThreads()
// 查询是否有些线程正在等待与此锁有关的给定条件。
boolean hasWaiters(Condition condition)
// 若是是“公平锁”返回true,不然返回false。
boolean isFair()
// 查询当前线程是否保持此锁。
boolean isHeldByCurrentThread()
// 查询此锁是否由任意线程保持。
boolean isLocked()
// 获取锁。
void lock()
// 若是当前线程未被中断,则获取锁。
void lockInterruptibly()
// 返回用来与此 Lock 实例一块儿使用的 Condition 实例。
Condition newCondition()
// 仅在调用时锁未被另外一个线程保持的状况下,才获取该锁。
boolean tryLock()
// 若是锁在给定等待时间内没有被另外一个线程保持,且当前线程未被中断,则获取该锁。
boolean tryLock(long timeout, TimeUnit unit)
// 试图释放此锁。
void unlock()
相关文章
相关标签/搜索