【并发技术13】条件阻塞Condition的应用(二)

简介: 【并发技术13】条件阻塞Condition的应用

可以考虑一个问题,为啥要用两个 Condition 呢?之所以这么设计肯定是有原因的:如果用一个 Condition,现在假设队列满了,但是有 2 个线程 A 和 B 同时存数据,那么都进入了睡眠,好,现在另一个线程取走一个了,然后唤醒了其中一个线程 A,那么 A 可以存了,存完后, A 又唤醒一个线程,如果 B 被唤醒了,那就出问题了,因为此时队列是满的,B 不能存的,B存的话就会覆盖原来还没被取走的只,这就是一个 Condition 带来的问题。

来测试一下上面的阻塞队列的效果。

public class BoundedBuffer {
    public static void main(String[] args) {        
        Buffer buffer = new Buffer();       
        for(int i = 0; i < 5; i ++) { //开启5个线程往缓冲区存数据
            new Thread(new Runnable() {             
                @Override
                public void run() {
                    try {
                        buffer.put(new Random().nextInt(1000)); //随机存数据
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        for(int i = 0; i < 10; i ++) { //开启10个线程从缓冲区中取数据
            new Thread(new Runnable() {             
                @Override
                public void run() {
                    try {
                        buffer.take(); //从缓冲区取数据
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}


我故意只开启 5 个线程存数据,10 个线程取数据,就是想让它出现取数据被阻塞的情况发生,看运行的结果。

Thread-5 被阻塞了,暂时无法取数据!

Thread-10 被阻塞了,暂时无法取数据!

Thread-1 存好了值: 755

Thread-0 存好了值: 206

Thread-2 存好了值: 741

Thread-3 存好了值: 381

Thread-14 取出了值: 755

Thread-4 存好了值: 783

Thread-6 取出了值: 206

Thread-7 取出了值: 741

Thread-8 取出了值: 381

Thread-9 取出了值: 783

Thread-5 被阻塞了,暂时无法取数据!

Thread-11 被阻塞了,暂时无法取数据!

Thread-12 被阻塞了,暂时无法取数据!

Thread-10 被阻塞了,暂时无法取数据!

Thread-13 被阻塞了,暂时无法取数据!

从结果中可以看出,线程 5 和10 抢先执行,发现队列中没有,于是就被阻塞了,睡在那了,知道队列中有新的值存入才可以取,但是他们两运气不好,存的数据又被其他线程前线取走了……可以多运行几次。如果想要看到存数据被阻塞,可以将取数据的线程设置少一点,这里我就不设了。


2.2 两个以上线程之间的唤醒


还是原来那个题目,现在让三个线程来执行,看一下题目:

有三个线程,子线程1先执行10次,然后子线程2执行10次,然后主线程执行5次,然后再切换到子线程1执行10次,子线程2执行10次,主线程执行5次……如此往返执行50次。

如果不用 Condition,还真不好弄,但是用 Condition 来做的话,就非常方便了,原理很简单,定义三个 Condition,子线程 1 执行完唤醒子线程 2,子线程 2 执行完唤醒主线程,主线程执行完唤醒子线程1。唤醒机制和上面那个缓冲区道理差不多,下面附上代码。

public class ThreeConditionCommunication {
    public static void main(String[] args) {
        Business bussiness = new Business();
        new Thread(new Runnable() {// 开启一个子线程
                    @Override
                    public void run() {
                        for (int i = 1; i <= 50; i++) {
                            bussiness.sub1(i);
                        }
                    }
                }).start();
        new Thread(new Runnable() {// 开启另一个子线程
            @Override
            public void run() {
                for (int i = 1; i <= 50; i++) {
                    bussiness.sub2(i);
                }
            }
        }).start();
        // main方法主线程
        for (int i = 1; i <= 50; i++) {
            bussiness.main(i);
        }
    }
    static class Business {
        Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition(); //Condition是在具体的lock之上的
        Condition condition2 = lock.newCondition();
        Condition conditionMain = lock.newCondition();
        private int bShouldSub = 0;
        public void sub1(int i) {
            lock.lock();
            try {
                while (bShouldSub != 0) {
                    try {
                        condition1.await(); //用condition来调用await方法
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 10; j++) {
                    System.out.println("sub1 thread sequence of " + j
                            + ", loop of " + i);
                }
                bShouldSub = 1;
                condition2.signal(); //让线程2执行
            } finally {
                lock.unlock();
            }
        }
        public void sub2(int i) {
            lock.lock();
            try {
                while (bShouldSub != 1) {
                    try {
                        condition2.await(); //用condition来调用await方法
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 10; j++) {
                    System.out.println("sub2 thread sequence of " + j
                            + ", loop of " + i);
                }
                bShouldSub = 2;
                conditionMain.signal(); //让主线程执行
            } finally {
                lock.unlock();
            }
        }
        public void main(int i) {
            lock.lock();
            try {
                while (bShouldSub != 2) {
                    try {
                        conditionMain.await(); //用condition来调用await方法
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                for (int j = 1; j <= 5; j++) {
                    System.out.println("main thread sequence of " + j
                            + ", loop of " + i);
                }
                bShouldSub = 0;
                condition1.signal(); //让线程1执行
            } finally {
                lock.unlock();
            }
        }
    }
}


关于线程中 Condition 技术就分享这么多吧。


相关文章
|
运维 Ubuntu Linux
【树莓派4B安装18.04桌面+远程SSH】
【树莓派4B安装18.04桌面+远程SSH】
734 0
|
6月前
|
人工智能 自然语言处理 小程序
技术小白如何利用DeepSeek半小时开发微信小程序?
通过通义灵码的“AI程序员”功能,即使没有编程基础也能轻松创建小程序或网页。借助DeepSeek V3和R1满血版模型,用户只需用自然语言描述需求,就能自动生成代码并优化程序。例如,一个文科生仅通过描述需求就成功开发了一款记录日常活动的微信小程序。此外,通义灵码还提供智能问答模式,帮助用户解决开发中的各种问题,极大简化了开发流程,让普通人的开发体验更加顺畅。
1709 11
技术小白如何利用DeepSeek半小时开发微信小程序?
|
9月前
|
应用服务中间件 Linux 网络安全
nginx安装部署ssl证书,同时支持http与https方式访问
为了使HTTP服务支持HTTPS访问,需生成并安装SSL证书,并确保Nginx支持SSL模块。首先,在`/usr/local/nginx`目录下生成RSA密钥、证书申请文件及自签名证书。接着,确认Nginx已安装SSL模块,若未安装则重新编译Nginx加入该模块。最后,编辑`nginx.conf`配置文件,启用并配置HTTPS服务器部分,指定证书路径和监听端口(如20000),保存后重启Nginx完成部署。
2862 8
|
C# 开发者 前端开发
揭秘混合开发新趋势:Uno Platform携手Blazor,教你一步到位实现跨平台应用,代码复用不再是梦!
【8月更文挑战第31天】随着前端技术的发展,混合开发日益受到开发者青睐。本文详述了如何结合.NET生态下的两大框架——Uno Platform与Blazor,进行高效混合开发。Uno Platform基于WebAssembly和WebGL技术,支持跨平台应用构建;Blazor则让C#成为可能的前端开发语言,实现了客户端与服务器端逻辑共享。二者结合不仅提升了代码复用率与跨平台能力,还简化了项目维护并增强了Web应用性能。文中提供了从环境搭建到示例代码的具体步骤,并展示了如何创建一个简单的计数器应用,帮助读者快速上手混合开发。
359 0
|
机器学习/深度学习 人工智能 算法
构建未来:AI驱动的自适应网络安全防御系统
【2月更文挑战第27天】 在数字化进程加速的背景下,网络安全威胁日益增长,传统的安全防御机制逐渐显得力不从心。本文提出一种基于人工智能技术的自适应网络安全防御系统架构,旨在通过实时分析网络行为和自动调整防御策略来应对复杂多变的网络攻击。该系统利用机器学习算法对异常行为进行检测与分类,并结合深度学习技术实现攻击模式的预测和响应策略的动态优化。文章详细阐述了系统的设计理念、关键技术以及预期效能,展望了AI技术在网络安全领域应用的未来趋势。
|
10月前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
185 9
|
10月前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
10月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
11月前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
消息中间件 PHP 数据安全/隐私保护
docker安装rabbitmq并配置hyperf使用
通过以上步骤,您可以实现在Docker容器中安装和配置RabbitMQ,并在PHP的Hyperf框架中进行集成和使用。这种方式为开发者提供了快速部署并实现消息队列解决方案的能力,同时维持了环境与生产环境的一致性,从而为开发高效的分布式应用程序提供了便利。
286 3
docker安装rabbitmq并配置hyperf使用