高并发编程-使用wait和notifyAll进行线程间的通信3_多线程下的生产者消费者模型和notifyAll

简介: 高并发编程-使用wait和notifyAll进行线程间的通信3_多线程下的生产者消费者模型和notifyAll

20191031000606569.png


概述

高并发编程-线程通信_使用wait和notify进行线程间的通信2_多生产者多消费者导致程序假死原因分析 中分析了假死的原因,这里我们来看下改如何解决在多线程下出现的这个问题呢?

20191008232746942.png

解决办法

  1. 多线程情况用while而不是if 来判断条件是否满足
  2. notify —> notifyAll
package com.artisan.test;
import java.util.stream.Stream;
public class MultiProduceConsumerDemo2 {
    // 对象监视器-锁
    private final Object LOCK = new Object();
    // 是否生产出数据的标识
    private boolean isProduced = false;
    // volatile 确保可见性, 假设 i 就是生产者生产的数据
    private volatile int i = 0;
    public void produce() {
        synchronized (LOCK) {
            String msg = isProduced ? "已生产货物" : "没有货物可搬运";
            // while 每次被唤醒时都会先检查isProduced是否滿足條件再继续
            // 不能用if的原因:if它将不再判断isProduced是否滿足條件,直接继续,引发错误
            // 举个例子 t1 ,t2 都进入到了wait ,然后使用if, 唤醒了t2后,不再判断isProduced是否滿足條件
            // 直接又生产了一个,导致生产多了数据
            while (isProduced) {
                try {
                    System.out.println(Thread.currentThread().getName() + " GOT LOCK ,isProduced= " + isProduced + " wait becauseof  " + msg);
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            i++;
            System.out.println(Thread.currentThread().getName() + " GOT LOCK ,isProduced= " + isProduced + " Produce:" + i);
            // 唤醒所有正在等待这个对象的monitor的线程
            LOCK.notifyAll();
            isProduced = true;
        }
    }
    public void consume() {
        // 加锁
        synchronized (LOCK) {
            String msg = isProduced ? "已生产货物" : "没有货物可搬运";
            // while 每次被唤醒时都会先检查isProduced是否滿足條件再继续
            while (!isProduced) {
                try {
                    System.out.println(Thread.currentThread().getName() + " wait becauseof  " + msg);
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + " GOT LOCK ,isProduced= " + isProduced + " Consume:" + i);
            //唤醒所有正在等待这个对象的monitor的线程
            LOCK.notifyAll();
            isProduced = false;
        }
    }
    public static void main(String[] args) {
        MultiProduceConsumerDemo2 produceConsumerDemo = new MultiProduceConsumerDemo2();
        Stream.of("P1", "P2").forEach(n -> new Thread(n) {
            @Override
            public void run() {
                while (true) produceConsumerDemo.produce();
            }
        }.start());
        Stream.of("C1", "C2").forEach(n -> new Thread(n) {
            @Override
            public void run() {
                while (true) produceConsumerDemo.consume();
            }
        }.start());
    }
}


相关文章
|
1月前
|
缓存 NoSQL 中间件
Redis的线程模型
Redis采用单线程模型确保操作的原子性,每次只执行一个操作,避免并发冲突。它通过MULTI/EXEC事务机制、Lua脚本和复合指令(如MSET、GETSET等)保证多个操作要么全成功,要么全失败,确保数据一致性。Redis事务在EXEC前失败则不执行任何操作,EXEC后失败不影响其他操作。Pipeline虽高效但不具备原子性,适合非热点时段的数据调整。Redis 7引入Function功能,支持函数复用,简化复杂业务逻辑。总结来说,Redis的单线程模型简单高效,适用于高并发场景,但仍需合理选择指令执行方式以发挥其性能优势。
57 6
|
2月前
|
存储 缓存 关系型数据库
MySQL底层概述—3.InnoDB线程模型
InnoDB存储引擎采用多线程模型,包含多个后台线程以处理不同任务。主要线程包括:IO Thread负责读写数据页和日志;Purge Thread回收已提交事务的undo日志;Page Cleaner Thread刷新脏页并清理redo日志;Master Thread调度其他线程,定时刷新脏页、回收undo日志、写入redo日志和合并写缓冲。各线程协同工作,确保数据一致性和高效性能。
MySQL底层概述—3.InnoDB线程模型
|
2月前
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
83 20
|
2月前
|
安全 Java C#
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
|
4月前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
112 1
Java—多线程实现生产消费者
|
4月前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
103 1
|
5月前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
132 9
|
5月前
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
158 0
|
2月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
67 17
|
2月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
67 26

热门文章

最新文章