ReentrantLock-基础

本文深入探讨了JDK中独占锁的实现方式,包括内部锁synchronized与ReentrantLock的对比,分析了它们在性能、灵活性及应用场景上的区别。同时,文章详细解释了公平锁与非公平锁的概念,以及ReentrantLock如何实现可响应中断和限时等待,最后介绍了ReentrantLock结合Condition接口实现的等待通知机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在JDK中独占锁的实现除了内部锁Synchronized,还可以使用ReentrantLock。

内部锁与可重入锁的对比
  1. 在JDK1.6以后,性能上两者差距已经不明显,因为对synchronized底层进行了优化,很多地方采用了CAS操作。在Java虚拟机上更偏向于内部锁的使用,官方也提倡在满足条件的情况下,优先选择内部锁。
  2. 两者都是独占锁,只允许一个线程进入临界区。synchronized的加锁释放锁的操作是隐式的,使用简单,但不够灵活,一般并发场景使用synchronized关键字可以满足需求;ReetrantLock需要手动加锁释放锁,释放锁的代码要在finally中代码块中,保证线程可以释放锁。在复杂并发场景中,可以使用ReentrantLock。
  3. 两者都是可重入锁。内部锁可以放在递归方法上,不用担心线程最后是否释放锁,ReentrantLock在重入时需要确保重复获取锁的次数和重复释放锁的次数一样,否则会导致其他线程无法获得锁。
  4. ReentrantLock可以实现公平锁,可响应中断和限时等待。
公平锁与非公平锁

公平锁是指当锁可用时,在锁上等待时间最长的线程将获得锁的使用权。在创建ReentrantLock的时候通过传进参数true创建公平锁,如果传入的是false或没传参数则创建的是非公平锁。

private static ReentrantLock lock = new ReentrantLock(true);   //公平锁
//    private static ReentrantLock lock = new ReentrantLock(false);   //公平锁


    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new FairSyncDemo(i)).start();
        }
    }

    static class FairSyncDemo implements Runnable{

        Integer id;

        public FairSyncDemo(Integer id) {
            this.id = id;
        }

        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for (int i = 0; i < 2; i++) {
                lock.lock();
                System.out.println("获得锁的线程:"+id);
                lock.unlock();
            }
        }
    }
//公平锁运行结果
获得锁的线程:2
获得锁的线程:0
获得锁的线程:3
获得锁的线程:1
获得锁的线程:4
获得锁的线程:2
获得锁的线程:0
获得锁的线程:3
获得锁的线程:1
获得锁的线程:4
//非公平锁运行结果
获得锁的线程:3
获得锁的线程:0
获得锁的线程:0
获得锁的线程:4
获得锁的线程:4
获得锁的线程:3
获得锁的线程:1
获得锁的线程:1
获得锁的线程:2
获得锁的线程:2

线程会重复获取锁。如果申请获取锁的线程足够多,那么可能会造成某些线程长时间得不到锁。这就是非公平锁的“饥饿”问题。大部分情况下我们使用非公平锁,因为其性能比公平锁好很多。但是公平锁能够避免线程饥饿,某些情况下也很有用。

可响应中断

当使用synchronized实现锁时,阻塞在锁上的线程除非获得锁否则将一直等待下去,也就是说这种无限等待获取锁的行为无法被中断。而ReentrantLock给我们提供了一个可以响应中断的获取锁的方法lockInterruptibly()。该方法可以用来解决死锁问题。

获取锁时限时等待

ReentrantLock还给我们提供了获取锁限时等待的方法tryLock(),可以选择传入时间参数,表示等待指定的时间,无参则表示立即返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。我们可以使用该方法配合失败重试机制来更好的解决死锁问题。

Condition

ReentrantLock结合Condition接口同样可以实现等待通知机制。而且相比Synchronized使用起来更清晰也更简单。

public class ReentrantLockTest {
    
    static BlockingQueue<Integer> queue = new BlockingQueue(5);
    Condition notFull = lock.newCondition();
    Condition notEmpty = lock.newCondition();
    
    public static void main(String[] args) {
        test2();
    }

    //测试queue
    public static void test2() {
        for (int i = 0; i < 10; i++) {
            int data = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        queue.enqueue(data);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        queue.dequeue();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

    static class BlockingQueue<E> {
        Lock lock = new ReentrantLock();
        Condition notFull = lock.newCondition();
        Condition notEmpty = lock.newCondition();

        List<E> list = new LinkedList<>();
        int size;

        public BlockingQueue(int size) {
            this.size = size;
        }

        public void enqueue(E e) throws InterruptedException {
            lock.lock();
            try {
                while (list.size() >= size)
                    notFull.await();
                list.add(e);
                System.out.println("进队列" + e);
                notEmpty.signal();

            } finally {
                lock.unlock();
            }
        }

        public E dequeue() throws InterruptedException {
            lock.lock();
            try {
                while (list.size() == 0)
                    notEmpty.await();
                E e = list.remove(0);
                System.out.println("出队列" + e);
                notFull.signal();
                return e;
            } finally {
                lock.unlock();
            }
        }

    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值