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;
    }

    @Override
    public 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;
    }

    @Override
    public 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            v
               Thread block     Thread unpark

        至此,全文结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

swadian2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值