【Java】-多线程synchronized与ReentrantLock和Condition的对比

298 阅读2分钟

本文主要讲解使用synchronized与ReentrantLock和Condition实现生产消费者的区别。

一、synchronized简介

Java程序依靠synchronized对线程进行同步。所以首先了解下synchronized的作用:

关键字 synchronized 可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一时刻,只能有一个线程处于方法或同步块中,它保证了线程对变量访问的可见性和排他性。

  • 对于普通同步方法,锁是当前实例对象
  • 对于静态同步方法,锁是当前类的 Class 对象
  • 对于同步方法块,锁是 Synchonized 括号里配置的对象

使用ReentrantLock比直接使用synchronized更安全,可以替代synchronized进行线程同步。

二、ReentrantLock介绍

顾名思义,ReentrantLock是可重入锁,它和synchronized一样,一个线程可以多次获取同一个锁。Java语言直接提供了synchronized关键字用于加锁,但这种锁一是很重,二是获取时必须一直等待,没有额外的尝试机制。java.util.concurrent.locks包提供的ReentrantLock用于替代synchronized加锁。

三、代码示例

package org.thread;

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

/**
 * 2022/11/24
 * 用synchronized实现的功能通过ReentrantLock和Condition来实现.
 * 使用Condition对象来实现wait和notify的功能。
 * @author Gerry
 * @version 1.0
 */
public class ConditionDemo {

    /**
     * 1.lock不再用synchronize把同步代码包装起来;
     * 2.阻塞需要另外一个对象condition;
     * 3.同步和唤醒的对象是condition而不是lock,对应的方法是await和signal,而不是wait和notify。
     *
     * 为什么需要使用condition呢?简单一句话,lock更灵活。以前的方式只能有一个等待队列,在实际应用时可能需要多个,
     * 比如读和写。为了这个灵活性,lock将同步互斥控制和等待队列分离开来,
     * 互斥保证在某个时刻只有一个线程访问临界区(lock自己完成),等待队列负责保存被阻塞的线程(condition完成)。
     */

    private final ReentrantLock lock = new ReentrantLock();

    //使用Condition时,引用的Condition对象必须从Lock实例的newCondition()返回,
    // 这样才能获得一个绑定了Lock实例的Condition实例
    //通过查看ReentrantLock的源代码发现,condition其实是等待队列的一个管理者,condition确保阻塞的对象按顺序被唤醒。
    private final Condition condition = lock.newCondition();

    private Queue<String> queue = new LinkedList<>();

    public void addTask(String s) {
        lock.lock();
        try {
            queue.add(s);
            condition.signal();
        } finally {
            //最后一定要释放
            lock.unlock();
        }
    }

    public String getTask() throws InterruptedException{
        lock.lock();
        try {
            while (queue.isEmpty()) {
                condition.await();
            }
            return queue.remove();
        } finally {
            //最后一定要释放
            lock.unlock();

        }
    }

    /**
     * -- 旧的方式:--
     * 线程consumer
     * synchronize(obj){
     *     obj.wait();//没东西了,等待
     * }
     *
     * 线程producer
     * synchronize(obj){
     *     obj.notify();//有东西了,唤醒
     * }
     *
     * -- 新的方式:--
     * //生产
     * lock.lock();
     * condition.await();
     * lock.unlock();
     *
     * //消费
     * lock.lock();
     * condition.signal();
     * lock.unlock();
     */
}