欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > Java中的锁机制:ReentrantLock 和 synchronized

Java中的锁机制:ReentrantLock 和 synchronized

2025/2/25 12:39:41 来源:https://blog.csdn.net/swadian2008/article/details/142655666  浏览:    关键词:Java中的锁机制:ReentrantLock 和 synchronized

目录

1、Synchronized和ReentrantLock的区别

2、线程协作和等待/通知机制

3、Synchronized的底层是如何实现的?

4、什么是锁的升级和降级?


1、Synchronized和ReentrantLock的区别

         Synchronized 和 ReentrantLock 都是Java中用于实现线程同步的工具。

        其中,Synchronized 是 Java 的一种语言级别的同步机制,使用简单,可以用于方法或代码块,它会自动获取和释放锁。因而语法较为简洁,不容易出错。

        ReentrantLock 是一个在 java.util.concurrent.locks 包中提供的显式锁。它需要手动获取和释放锁,通常使用 lock() 和 unlock() 方法。但是 ReentrantLock 也提供了更多的功能,如尝试获取锁、定时锁、响应中断请求等。

        此外,Synchronized 默认是非公平的,它无法保证锁的获取顺序。而 ReentrantLock 可以选择公平锁和非公平锁,使用公平锁会按照线程请求的顺序来获取锁。

2、线程协作和等待/通知机制

        Synchronized中的等待和通知机制

        在 Synchronized 中使用 wait() 和 notify() 方法来实现条件等待和通知机制。wait() 和 notify() 方法是 Java 中用于线程间通信的关键机制,通常与 synchronized 关键字结合使用。

        以下是一个简单的示例(生产者-消费者模式),展示如何使用这两个方法。

import java.util.LinkedList;
import java.util.Queue;/*** 共享资源*/
class SharedResource {private final Queue<Integer> queue = new LinkedList<>();/*** 生产资源** @param value* @throws InterruptedException*/public synchronized void produce(int value) throws InterruptedException {int LIMIT = 5;while (queue.size() == LIMIT) {wait(); // 等待,直到有空间可用}queue.add(value);System.out.println("Produced: " + value);notify(); // 通知消费者可以消费了}/*** 消费资源** @throws InterruptedException*/public synchronized void consume() throws InterruptedException {while (queue.isEmpty()) {wait(); // 等待,直到有数据可用}int value = queue.poll();System.out.println("Consumed: " + value);notify(); // 通知生产者可以生产了}
}/*** 生产者*/
class Producer implements Runnable {private final SharedResource resource;public Producer(SharedResource resource) {this.resource = resource;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {resource.produce(i);Thread.sleep(100); // 模拟生产过程} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}/*** 消费者*/
class Consumer implements Runnable {private final SharedResource resource;public Consumer(SharedResource resource) {this.resource = resource;}@Overridepublic void run() {for (int i = 0; i < 10; i++) {try {resource.consume();Thread.sleep(150); // 模拟消费过程} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}public class WaitNotifyExample {public static void main(String[] args) {SharedResource resource = new SharedResource();Thread producerThread = new Thread(new Producer(resource));Thread consumerThread = new Thread(new Consumer(resource));producerThread.start();consumerThread.start();}
}

        wait() 和 notify() 方法在多线程编程中非常有用,能够有效地实现线程间的协作。

        ReentrantLock中的等待和通知机制

       ReentrantLock 相比synchronized,可以像普通对象一样使用,在ReentrantLock的等待和通知机制中,Java提供了Condition对象,从而可以更加灵活地实现此机制。

        下边代码示例,通过Condition对象简单实现了 ReentrantLock 中的等待和通知机制,用法比较简单:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class ConditionExample {//步骤一:首先,创建一个ReentrantLock实例和与之关联的Condition对象。private final ReentrantLock lock = new ReentrantLock();private final Condition condition = lock.newCondition();private int count = 0;//步骤二:在某个条件不满足时,使用await()方法让当前线程等待,同时释放锁。public void awaitCondition() {lock.lock();try {while (count < 5) {condition.await();}System.out.println("Count reached: " + count);} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {lock.unlock();}}//步骤三:在条件满足时,使用signal()或signalAll()方法通知等待的线程。public void incrementAndSignal() {lock.lock();try {count++;System.out.println("Count incremented to: " + count);if (count >= 5) {condition.signalAll();}} finally {lock.unlock();}}public static void main(String[] args) {ConditionExample example = new ConditionExample();// 创建线程等待条件new Thread(example::awaitCondition).start();// 模拟计数增量for (int i = 0; i < 10; i++) {example.incrementAndSignal();try {Thread.sleep(500); // 模拟时间间隔} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}

        使用 ReentrantLock 和 Condition 可以实现灵活的线程同步机制,允许线程在特定条件下等待并在条件满足时被唤醒。这种机制在需要复杂的线程协作时非常有用。

3、Synchronized的底层是如何实现的?

        Synchronized 其底层实现依赖于 Java 虚拟机(JVM)和操作系统的支持。

        具体来说,Synchronized 代码块是由一对 monitorenter/monitorexit 指令实现的,Java 通过这些指令负责进行加锁和解锁。Monitor 对象是同步的基本实现单元。

        在 Java 6 之前,Monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作。

        现代的(Oracle)JDK 中,JVM 对此进行了改进,提供了三种不同的 Monitor 实现,也就是常说的三种不同的锁:偏斜锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。

4、什么是锁的升级和降级?

        所谓锁的升级、降级,就是 JVM 优化 Synchronized 运行的机制,当 JVM 检测到不同的竞争状况时,会自动切换到适合的锁实现,这种切换就是锁的升级、降级。

        当没有竞争出现时,默认会使用偏斜锁。JVM 会利用 CAS 操作(compare and swap),在对象头上的 Mark Word 部分设置线程 ID,以表示这个对象偏向于当前线程,所以并不涉及真正的互斥锁。这样做的假设是基于在很多应用场景中,大部分对象生命周期中最多会被一个线程锁定,使用偏斜锁可以降低无竞争开销。

        如果有另外的线程试图锁定某个已经被偏斜过的对象,JVM 就需要撤销(revoke)偏斜锁,并切换到轻量级锁实现。轻量级锁依赖 CAS 操作 Mark Word 来试图获取锁,如果重试成功,就使用普通的轻量级锁;否则,进一步升级为重量级锁。

        当 JVM 进入安全点(SafePoint)的时候,会检查是否有闲置的 Monitor,然后试图进行降级。

5、ReentrantLock 的实现原理

        ReentrantLock 比 Synchronized 提供了更多的功能和灵活性,比如公平锁、非公平锁、可中断的锁等待等。它的实现依赖于 AQS(AbstractQueuedSynchronizer),这是 Java 实现锁和其他同步机制的核心类。

        ReentrantLock 通过继承自 AbstractQueuedSynchronizer(AQS)来实现锁的语义。AQS 使用一个整数值 state 来表示锁的状态,并通过 CAS 操作(Compare-And-Swap)实现状态的原子更新。线程通过 CAS 操作来获取锁、释放锁,并在争夺锁失败时进入阻塞队列。

        AQS 的工作原理总结图解:

                 +-------------+acquire   |             |   tryAcquire+---------->|   AQS Lock  |<--------------+|           |             |               ||           +------+------|               ||                  |                      ||          acquire |                      ||             fail |                      ||                  v                      ||           +-------------+               |+---------->|  WaitQueue  |   release     |+------+------+<--------------+|            |v            vThread block     Thread unpark

        至此,全文结束。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词