寒假打卡15-1月30日

Java ReentrantLock——高级锁机制详解

在 Java 并发编程中,synchronized 关键字提供了一种简单的锁机制来保护共享资源,但它具有一些局限性,例如不支持公平锁、无法中断等待锁的线程等。Java 提供了 ReentrantLock 类来克服这些局限性,并提供更高级的锁机制。

ReentrantLock 的基本概念

ReentrantLock 是 java.util.concurrent.locks 包中的一个类,它实现了 Lock 接口。与 synchronized 不同,ReentrantLock 提供了更多的锁操作选项,如公平锁、可重入锁、可中断锁和定时锁等。

ReentrantLock 的主要特性

  • 可重入性:与 synchronized 类似,ReentrantLock 也支持可重入性,即同一个线程可以多次获取同一个锁。
  • 公平锁ReentrantLock 支持公平锁和非公平锁,公平锁按线程请求锁的顺序获取锁,非公平锁则可能导致线程饥饿。
  • 可中断锁ReentrantLock 提供了可以中断的锁获取操作,允许线程在等待锁时响应中断。
  • 定时锁ReentrantLock 提供了定时锁获取操作,允许线程在等待锁时设置超时时间。

ReentrantLock 的使用方法

基本使用

下面是一个使用 ReentrantLock 保护共享资源的简单示例:

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();
    private int counter = 0;

    public void increment() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public int getCounter() {
        lock.lock();
        try {
            return counter;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ReentrantLockDemo demo = new ReentrantLockDemo();
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                demo.increment();
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);
        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Counter: " + demo.getCounter());
    }
}

在上述代码中,lock.lock()lock.unlock() 用于获取和释放锁,以确保 counter 的递增操作是线程安全的。

公平锁

ReentrantLock 构造函数可以接受一个布尔参数来指定是否启用公平锁:

ReentrantLock fairLock = new ReentrantLock(true);

公平锁按线程请求锁的顺序获取锁,避免了线程饥饿。

可中断锁

ReentrantLock 提供了 lockInterruptibly() 方法,可以在等待锁时响应中断:

public void incrementInterruptibly() throws InterruptedException {
    lock.lockInterruptibly();
    try {
        counter++;
    } finally {
        lock.unlock();
    }
}

定时锁

ReentrantLock 提供了 tryLock(long timeout, TimeUnit unit) 方法,可以在等待锁时设置超时时间:

public boolean tryIncrement(long timeout, TimeUnit unit) throws InterruptedException {
    if (lock.tryLock(timeout, unit)) {
        try {
            counter++;
            return true;
        } finally {
            lock.unlock();
        }
    } else {
        return false;
    }
}

ReentrantLock 的高级用法

条件变量

ReentrantLock 提供了条件变量(Condition)来实现线程间的通信和协作。条件变量类似于 Objectwaitnotify 方法,但提供了更强大的功能。

示例代码

下面是一个使用条件变量实现生产者-消费者模式的示例:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.LinkedList;
import java.util.Queue;

public class ProducerConsumerDemo {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 10;

    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                notFull.await();
            }
            queue.offer(value);
            notEmpty.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public int consume() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await();
            }
            int value = queue.poll();
            notFull.signalAll();
            return value;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ProducerConsumerDemo demo = new ProducerConsumerDemo();

        Runnable producerTask = () -> {
            for (int i = 0; i < 20; i++) {
                try {
                    demo.produce(i);
                    System.out.println("Produced: " + i);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        };

        Runnable consumerTask = () -> {
            for (int i = 0; i < 20; i++) {
                try {
                    int value = demo.consume();
                    System.out.println("Consumed: " + value);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        };

        Thread producerThread = new Thread(producerTask);
        Thread consumerThread = new Thread(consumerTask);
        producerThread.start();
        consumerThread.start();

        try {
            producerThread.join();
            consumerThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,生产者线程和消费者线程通过条件变量 notFullnotEmpty 实现线程间的通信和协作。生产者在队列满时等待,消费者在队列空时等待。

总结

ReentrantLock 提供了一种灵活且功能强大的锁机制,克服了 synchronized 的一些局限性。它支持公平锁、可中断锁、定时锁和条件变量,适用于多种并发编程场景。通过合理使用 ReentrantLock,我们可以编写出更加高效和健壮的多线程程序。

希望通过本篇文章,大家对 Java ReentrantLock 有了更深入的了解。在接下来的文章中,我们将继续探讨更多关于 Java 并发编程的知识点,敬请期待!

posted @ 2025-01-30 09:10  aallofitisst  阅读(18)  评论(0)    收藏  举报