11月11, 2019

java ReentrantLock

ReentrantLock和synchronized的相同点

1.ReentrantLock和synchronized都是独占锁,只允许线程互斥的访问临界区。但是实现上两者不同:synchronized加锁解锁的过程是隐式的,用户不用手动操作,优点是操作简单,但显得不够灵活。一般并发场景使用synchronized的就够了;ReentrantLock需要手动加锁和解锁,且解锁的操作尽量要放在finally代码块中,保证线程正确释放锁。ReentrantLock操作较为复杂,但是因为可以手动控制加锁和解锁过程,在复杂的并发场景中能派上用场。

2.ReentrantLock和synchronized都是可重入的。synchronized因为可重入因此可以放在被递归执行的方法上,且不用担心线程最后能否正确释放锁;而ReentrantLock在重入时要却确保重复获取锁的次数必须和重复释放锁的次数一样,否则可能导致其他线程无法获得该锁。

ReentrantLock公平锁

公平锁是指当锁可用时,在锁上等待时间最长的线程将获得锁的使用权。而非公平锁则随机分配这种使用权。和synchronized一样,默认的ReentrantLock实现是非公平锁,因为相比公平锁,非公平锁性能更好。当然公平锁能防止饥饿,某些情况下也很有用。在创建ReentrantLock的时候通过传进参数true创建公平锁,如果传入的是false或没传参数则创建的是非公平锁

例子

public class Java_ReentrantLock {

    public void test() throws InterruptedException {
        // ReentrantLock是独占锁且可重入的
        ReentrantLock lock = new ReentrantLock();
        for (int i = 1; i <= 3; i++) {
            lock.lock();
        }
        for (int i = 1; i <= 3; i++) {
            try {
            } finally {
                lock.unlock();
            }
        }
    }

    public void test1() {
        // 场景 1:如果发现该操作已经在执行中则不再执行(有状态执行)
        ReentrantLock lock = new ReentrantLock();
        if (lock.tryLock()) { // 如果已经被 lock,则立即返回 false 不会等待,达到忽略操作的效果。
            try {
                // 操作
            } finally {
                lock.unlock();
            }

        }
    }

    public void test2() {
        // 场景 2:如果发现该操作已经在执行,等待一个一个执行(同步执行,类似 synchronized)
        // 公平情况下,操作会排一个队按顺序执行,来保证执行顺序。(会消耗更多的时间来排队)
        // 不公平情况下,是无序状态允许插队,JVM 会自动计算如何处理更快速来调度插队。(如果不关心顺序,这个速度会更快)
        ReentrantLock lock = new ReentrantLock(); // 参数默认false,不公平锁
        // ReentrantLock lock = new ReentrantLock(true); //公平锁
        try {
            lock.lock(); // 如果被其它资源锁定,会在此等待锁释放,达到暂停的效果
            // 操作
        } finally {
            lock.unlock();
        }
    }

    public void test3() {
        // 场景 3:如果发现该操作已经在执行,则尝试等待一段时间,等待超时则不执行(尝试等待执行)
        ReentrantLock lock = new ReentrantLock();
        try {
            if (lock.tryLock(5, TimeUnit.SECONDS)) { // 如果已经被 lock,尝试等待
                                                        // 5s,看是否可以获得锁,如果 5s
                                                        // 后仍然无法获得锁则返回 false
                                                        // 继续执行
                try {
                    // 操作
                } finally {
                    lock.unlock();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace(); // 当前线程被中断时(interrupt),会抛 InterruptedException
        }
    }

    public void test4() {
        // 场景 5:条件判断。
        // 每一个 lock 可以有任意数据的 Condition 对象
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        lock.lock();
        try {
            boolean cnd = false;
            while (cnd) {
                condition.wait();
            }
            // 处理逻辑
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

本文链接:https://blog.jnliok.com/post/java-reentrantlock.html

-- EOF --

Comments