Java多线程7:ReentrantLock()锁的详细介绍——学习方腾飞Java并发编程的艺术

ReentrantLock

使用synchronized关键字会隐式地创建锁,但是却将锁的获取和释放固定化了。当然这种方式也便捷了程序员不用手动锁定和释放锁,但是也带来了一定的不方便,而用Lock却可以更加的灵活。
重入锁ReentrantLock,支持重进入的锁,表示该锁能够支持一个线程对资源的重复加锁。在其调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。并且ReentrantLock还支持获取锁时的公平和非公平锁

公平和非公平锁

公平锁就是严格按照请求时间,就是等待时间最长的线程最优先获取锁,而非公平锁却不安这个顺序来获取锁。其实公平的锁机制往往没有非公平的效率高,但是公平锁能够减少“饥饿“发生的概率。

Lock接口提供的synchronized关键字不具备的特性

1、尝试非阻塞地获取锁:当前线程尝试获取锁,如果这一时刻没有被其他线程获取,则成功获取并持有锁
2、能被中断的获取锁:于synchronized不同,获取到锁的线程能响应中断,当获取到锁的线程被中断时,中断异常被抛出,同事锁被释放
3、超时获取锁:在指定的截止时间之前获取锁,若没获取到,则返回

ReentrantLock锁的释放

ReentrantLock锁的释放是逐级释放的,也就是说在可重入性场景中,必须要等到场景内所有的加锁的方法都释放了锁,当前线程持有的锁才会被释放!

ReentrantLock用法

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

public class ThreadDomain04 {

    private Lock lock = new ReentrantLock(true);

    public void testMethod() {
        try {
            lock.lock();
            for (int i = 0; i < 2; i++) {
                System.out.println("ThreadName = " + Thread.currentThread().getName() +
                        ", i  = " + i);
            }
        } finally {
            lock.unlock();
        }
    }
}
public class MyThread08 {
    public static void main(String[] args) throws InterruptedException {
        final ThreadDomain04 th = new ThreadDomain04();
        Thread threadA = new Thread(new MyThread08.aRunner(th), "aThread");
        Thread threadB = new Thread(new MyThread08.aRunner(th), "bThread");
        Thread threadC = new Thread(new MyThread08.aRunner(th), "cThread");
        threadA.start();
        threadB.start();
        threadC.start();
    }

    static class aRunner implements Runnable {
        private ThreadDomain04 thd;

        public aRunner(ThreadDomain04 thd) {
            this.thd = thd;
        }

        @Override
        public void run() {
            {
                thd.testMethod();
            }
        }
    }
}
// ThreadName = bThread, i  = 0
// ThreadName = bThread, i  = 1
// ThreadName = aThread, i  = 0
// ThreadName = aThread, i  = 1
// ThreadName = cThread, i  = 0
// ThreadName = cThread, i  = 1

结果分组打印证明了ReentrantLock加锁的功能

ReentrantLock锁的对象监视器

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

public class ThreadDomain05 {

    private Lock lock = new ReentrantLock(true);

    public void testMethodA() throws InterruptedException {
        lock.lock();
        try {
            System.out.println("MethodA begin ThreadName = " + Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("MethodA end ThreadName = " + Thread.currentThread().getName());
        } finally {
            lock.unlock();
        }
    }

    public void testMethodB() throws InterruptedException {
        lock.lock();
        try {
            System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName());
            System.out.println("MethodB end ThreadName = " + Thread.currentThread().getName());
        } finally {
            lock.unlock();
        }
    }
}
public class MyThread09 {
    public static void main(String[] args) throws InterruptedException {
        final ThreadDomain05 th = new ThreadDomain05();
        Thread threadA = new Thread(new MyThread09.aRunner(th), "aThread");
        Thread threadB = new Thread(new MyThread09.bRunner(th), "bThread");
        threadA.start();
        threadB.start();
    }

    static class aRunner implements Runnable {
        private ThreadDomain05 thd;

        public aRunner(ThreadDomain05 thd) {
            this.thd = thd;
        }

        @Override
        public void run() {
            {
                try {
                    thd.testMethodA();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    static class bRunner implements Runnable {
        private ThreadDomain05 thd;

        public bRunner(ThreadDomain05 thd) {
            this.thd = thd;
        }

        @Override
        public void run() {
            {
                try {
                    thd.testMethodB();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
// MethodA begin ThreadName = aThread
// MethodA end ThreadName = aThread
// MethodB begin ThreadName = bThread
// MethodB end ThreadName = bThread

结果A线程begin和end之间的确经过了5s之后才会有B线程的启动,证明了ReentrantLock持有的是对象监视器。

注意: 不要讲获取锁的过程写在try块里面,因为如果在获取锁时发生了异常,异常抛出的同时会导致锁无故释放。

tryLock()和tryLock(long timeout, TimeUnit unit)

tryLock()方法的作用是,在调用try()方法的时候,如果锁没有被另外一个线程持有,那么就返回true,否则返回false
tryLock(long timeout, TimeUnit unit)是tryLock()另一个重要的重载方法,表示如果在指定等待时间内获得了锁,则返回true,否则返回false
注意一下,tryLock()只探测锁是否,并没有lock()的功能,要获取锁,还得调用lock()方法。

猜你喜欢

转载自blog.csdn.net/qq_22798455/article/details/81335066