简单聊聊Java中的ReentrantLock

602 阅读6分钟

【福利】关注微信公众号:深夜程猿,回复关键字即可获取学习视频、简历模版

欢迎读者批评指正~~

背景知识

在Java中,要想做到线程同步的传统方式是使用关键字synchronized。synchronized关键字提供了一个基本的同步机制,但是它的使用是十分严格的。例如,一个线程只能获取一次锁。同步的阻塞没有提供任何阻塞队列机制,当一个线程退出的时候,任何的线程都可以获取锁。这样会导致某些线程在一段长时间内处于饥饿状态(获取不到锁,无法访问资源)。可重入锁(Reentrant Lock)的引入使得解决这方面的问题变得简便,处理Java的线程同步问题也会加灵活。

什么是可重入锁

可重入锁实现了Lock接口,提供了一些共享资源的方法。操作共享资源的代码处于lock方法和unlock方法的调用之间。这使得当前工作线程获取锁,阻塞其他想要获取共享资源锁的线程。 正如名字那样,可重入锁使得线程可以多次进入资源锁(就是占有该资源的线程在为完全释放资源锁的时候,还可以再一次进入该资源锁)。当线程第一次进入锁的时候,会设置一个锁计数值(hold count)为1,在线程未释放锁的时候可以再一次进入该锁,锁计数值递增.对于每一次释放锁的请求,锁计数值会递减,直到锁计数值为0的时候,资源才会真正被释放。 可重入锁也会提供一个公平的参数,使得锁遵守锁的请求顺序。比如,在一个线程释放资源的时候,将锁分配给等待最长时间的线程。在构造可重入锁的时候,传入true就可以设置公平模式。 unlock方法要放在finally语句中调用,保证任何情况都会释放锁。

ReentrantLock的常用方法

lock()

调用lock方法,如果资源锁未被其它线程占用(当前线程占用还可以再一次获取锁)就获取资源锁,锁计数值递增,线程获得资源锁并立即;如果资源锁被其它线程占用,那么就进入休眠状态,直到获取到资源锁,同时锁计数值设为1;

tryLock()

调用tryLock方法,如果资源没有被其它任何线程占有(当前线程占用还可以再一次获取锁)就获取资源锁,返回true并递增锁计数值;如果资源锁被其它线程占用占用,返回false,线程不会退出,而是执行完run方法逻辑再尝试获取资源锁,依次下去,直到获取到资源锁;

tryLock(long timeout, TimeUnit unit)

作用和tryLock一样,只是为获取到资源锁的时候,线程会等待(不断尝试获取资源锁),超过设置的超时时间才会返回false;

lockInterruptibly()

调用这个方法,如果资源没有被其它任何线程占有(当前线程占用还可以再一次获取锁)就获取资源锁,递增锁计数值;如果再获取锁的过程,当前线程被其它线程interrupt(其它线程调用当前线程的interrupt()方法),那么就会要求处理InterruptedException;如果当前线程没有获取到锁,就会休眠。休眠过程,如果其它线程interupt当前线程,也会要求处理InterruptedException

unlock()

调用unlock方法会对锁计数值递减,只有锁计数值的值是0的时候才会释放资源

演示demo

public class Test {

    static class Worker implements Runnable {
        String name;
        ReentrantLock re;

        public Worker(ReentrantLock rl, String n) {
            re = rl;
            name = n;
        }

        public void run() {
            boolean done = false;
            while (!done) {
                //获取外部锁
                System.out.println("task name - " + name + " try to acquired lock by tryLock()");
                boolean ans = re.tryLock();
                if (ans) {
                    // 获取到锁
                    try {
                        Date d = new Date();
                        SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                        System.out.println("task name - " + name
                                + " outer lock acquired at "
                                + ft.format(d)
                                + " Doing outer work");
                        Thread.sleep(1500);

                        // 获取内部锁
                        System.out.println("task name - " + name + " try to acquired lock by lock()");
                        re.lock();
                        try {
                            d = new Date();
                            ft = new SimpleDateFormat("hh:mm:ss");
                            System.out.println("task name - " + name
                                    + " inner lock acquired at "
                                    + ft.format(d)
                                    + " Doing inner work");
                            System.out.println("Lock Hold Count - " + re.getHoldCount());
                            Thread.sleep(1500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            //释放内部锁
                            System.out.println("task name - " + name +
                                    " releasing inner lock");

                            re.unlock();
                        }
                        System.out.println("Lock Hold Count - " + re.getHoldCount());
                        System.out.println("task name - " + name + " work done");

                        done = true;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //释放外部锁
                        System.out.println("task name - " + name +
                                " releasing outer lock");

                        re.unlock();
                        System.out.println("Lock Hold Count - " +
                                re.getHoldCount());
                    }
                } else {
                    // 没有获取到锁
                    System.out.println("task name - " + name +
                            " waiting for lock");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    static final int MAX_T = 2;
    public static void main(String[] args) {
        ReentrantLock rel = new ReentrantLock();
        ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
        Runnable w1 = new Worker(rel, "Job1");
        Runnable w2 = new Worker(rel, "Job2");
        Runnable w3 = new Worker(rel, "Job3");
        Runnable w4 = new Worker(rel, "Job4");
        pool.execute(w1);
        pool.execute(w2);
        pool.execute(w3);
        pool.execute(w4);
        pool.shutdown();
    }
}
输出
task name - Job1 try to acquired lock by tryLock()
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 outer lock acquired at 05:29:50 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 try to acquired lock by lock()
task name - Job1 inner lock acquired at 05:29:51 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 releasing inner lock
Lock Hold Count - 1
task name - Job1 work done
task name - Job1 releasing outer lock
Lock Hold Count - 0
task name - Job3 try to acquired lock by tryLock()
task name - Job3 outer lock acquired at 05:29:53 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job3 try to acquired lock by lock()
task name - Job3 inner lock acquired at 05:29:54 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job3 releasing inner lock
Lock Hold Count - 1
task name - Job3 work done
task name - Job3 releasing outer lock
Lock Hold Count - 0
task name - Job4 try to acquired lock by tryLock()
task name - Job4 outer lock acquired at 05:29:56 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job4 try to acquired lock by lock()
task name - Job4 inner lock acquired at 05:29:57 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job4 releasing inner lock
Lock Hold Count - 1
task name - Job4 work done
task name - Job4 releasing outer lock
Lock Hold Count - 0
task name - Job2 try to acquired lock by tryLock()
task name - Job2 outer lock acquired at 05:30:00 Doing outer work
task name - Job2 try to acquired lock by lock()
task name - Job2 inner lock acquired at 05:30:01 Doing inner work
Lock Hold Count - 2
task name - Job2 releasing inner lock
Lock Hold Count - 1
task name - Job2 work done
task name - Job2 releasing outer lock
Lock Hold Count - 0