Java 多线程编程 生产者 消费者模式 (ReentrantLock锁 / Condition的等待通知机制 await等待 signal唤醒)

synchronized和 ReentrantLock的相同点

  • 两种锁都是独占锁, 只允许线程互斥的访问临界区
  • 两种锁都可以重入, 在一个线程可以重复获取多次锁
    两者区别是 synchronized重入不用考虑解锁相关问题, 而 ReentrantLock必须确保重复获取锁的次数和释放锁的次数相同, 否者相关锁无法解

synchronized和 ReentrantLock的区别

  • synchronized是加解锁是隐式的, ReentrantLock是手动加解锁
  • synchronized是通过 Object的 wait和 notify方法来实现线程间的等待通知机制, 但是 ReentrantLock是通过 Condition接口来实现了等待通知机制
  • ReentrantLock支持响应中断, 出现死锁现象可以通过 lockInterruptibly()方法响应中断. 但是 synchronized出现死锁时会无限等待,
  • ReentrantLock支持公平锁, 被锁后等了最长时间的线程将会优先解锁, 而默认是非公平锁就是随机的 synchronized只支持非公平锁
    • 非公平锁性能优于公平锁, 但是公平锁能防止线程饥饿现象发生. 创建方式: new ReentrantLock(true) 参数 true是创建公平锁, false或无参是非公平锁

1. 消息类

  • 消息数超过存放最大数, 则生产者线程阻塞(等待消费者消费后唤醒)
  • 消息数等于空, 则消费者线程阻塞(等待生产者生产后唤醒)

public class Message {
    /** 当前消息数量*/
    private int count = 0;
    /** 信息存放最大限数*/
    private int maximum = 20;
    /** 重入锁*/
    private Lock lock;
    /** 生产者锁控制器*/
    private Condition producerCondition;
    /** 消费者锁控制器*/
    private Condition consumerCondition;

    public Message() {}

    public Message(final Lock lock, final Condition producerCondition, final Condition consumerCondition) {
        this.lock = lock;
        this.producerCondition = producerCondition;
        this.consumerCondition = consumerCondition;
    }

    /**
     * 生产消息
     * */
    public void set() {
        /** 获取锁*/
        lock.lock();
        try {
            if (count <= maximum) {
                /** 生产一个消息*/
                System.out.println("生产者 线程" + Thread.currentThread().getName() + "生产了一个消息, 当前有" + (++count) + "个消息");
                /** 唤醒等待的消费者*/
                consumerCondition.signal();
            } else {
                try {
                    /**
                     * 如果当前消息大于 maximum信息最大数
                     * 生产者进入睡眠/等待状态
                     * */
                    producerCondition.await();
                    System.out.println("生产者 线程" + Thread.currentThread().getName() + "进入睡眠");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            /** 释放锁*/
            lock.unlock();
        }
    }

    /**
     * 消费消息
     * */
    public void get() {
        /** 获取锁*/
        lock.lock();
        try {
            if (count > 0) {
                /** 消费一个消息*/
                System.out.println("消费者 线程" + Thread.currentThread().getName() + "消费了一个消息, 当前有" + (--count) + "个消息");
                /** 唤醒等待的生产者*/
                producerCondition.signal();
            } else {
                try {
                    /** 如果没有消息, 消费者进入睡眠/等待状态*/
                    consumerCondition.await();
                    System.out.println("消费者 线程" + Thread.currentThread().getName() + "进入睡眠");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            /** 释放锁*/
            lock.unlock();
        }
    }

}

2. 生产者类


public class Producer implements Runnable {
    private Message message;
    public Producer(Message message) {
        this.message = message;
    }

    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            message.set();
        }
    }

}

3. 消费者类


public class Consumer implements Runnable {
    private Message message;
    public Consumer(Message message) {
        this.message = message;
    }

    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            message.get();
        }
    }

}

Runnable是接口, Thread是 Runnable的实现类, 由于 Java对多接口实现比较灵活, 开发多线程时建议实现 Runnable接口. 启动时用 Thread的 start(). 例: new Thread().start()

4. App.java


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class App {
    public static void main(String[] args) {
        /** 重入锁*/
        final Lock lock = new ReentrantLock();
        /** 生产者锁控制器*/
        final Condition producerCondition = lock.newCondition();
        /** 消费者锁控制器*/
        final Condition consumerCondition = lock.newCondition();
        final Message message = new Message(lock, producerCondition, consumerCondition);
        /** 建几个生产线程*/
        new Thread(new Producer(message)).start();
        new Thread(new Producer(message)).start();
        new Thread(new Producer(message)).start();
        /** 建几个消费线程*/
        new Thread(new Consumer(message)).start();
        new Thread(new Consumer(message)).start();
        new Thread(new Consumer(message)).start();
        new Thread(new Consumer(message)).start();
    }

}

如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

发布了62 篇原创文章 · 获赞 325 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qcl108/article/details/101841751