【Java开发】JUC基础 05:线程通信/协作

news2024/11/16 21:42:20

1 生产者消费者问题

📌 线程通信应用的场景可以简单地描述为生产者和消费者问题

  • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费;

  • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止;

  • 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。

📌 这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

  • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费;

  • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费;

📢 在生产者消费者问题中,仅有synchronized是不够的

  • synchronized可阻止并发更新同一个共享资源,实现了同步

  • synchronized不能用来实现不同线程之间的消息传递 (通信)!

2 线程通信

2.1 线程方法

方法名

作用

wait ()

表示线程一直等待,直到其他线程通知,与 sleep 不同会释放锁

wait (long timeOut)

指定等待的毫秒数

notify ()

唤醒一个处于等待状态的线程

notifyAll()

唤醒同一个对象所有的调用 wait()方法的线程,优先级高的优先调度

2.2 管程法

📌 要点

  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)

  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)

  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区

📢 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

📌 代码举例

//需要生产者、消费者、产品、缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }

}

//产品-鸡
class Chicken{
    int id;
    public Chicken(int id){
        this.id = id;
    }
}

//生产者
class Productor extends Thread{
    SynContainer container;
    public Productor(SynContainer container){
        this.container=container;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            container.pushTo(new Chicken(i));
            System.out.println("生产了第"+i+"只鸡");
        }
    }
}

//消费者
class Consumer extends Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container=container;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println("消费了第"+container.popTo().id+"只鸡,累积消费了第"+i+"只鸡");
        }
    }
}

class SynContainer{
    /**定义一个容器的大小*/
    Chicken[] chickens = new Chicken[5];
    /** 容器计数器*/
    int count;

    //生产者想容器中放入产品
    public synchronized  void pushTo(Chicken chicken){
        //如果容器满了,就需要等待消费者消费
        if(chickens.length==count){
            //通知消费者消费
            try {
                this.wait();
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有满就丢入产品
        chickens[count]=chicken;
        count++;
        //通知消费者消费
        this.notifyAll();
    }

    //消费者从中取出产品
    public synchronized Chicken popTo(){
        //判断能否消费
        if(count==0){
            // 消费者等待生产者产出
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];
        //吃完了通知生产者消费
        this.notifyAll();
        return chicken;
    }
}

控制台输出:

一开始两个线程都会进容器,不过消费者看到容器鸡的数量为0,就会等待;此时生产者进行生产,当容器满了后,生产者等待,通知消费者来消费,那么消费者进行消费,通知生产者可以继续生产...

📢 我把数量调小了,大家可以把数量增加,这样效果更显著。

2.3 信号灯法

简单来说,就是利用标识控制线程等待和唤醒,类似于信号灯,红灯性绿灯停。

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者--演员
class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            if (i%2==0){
                this.tv.play("电视节目:"+i);
            }else {
                this.tv.play("电视广告:"+i);
            }
        }
    }
}

//消费者--观众
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            this.tv.watch();
        }
    }
}

//产品--节目
class TV{
    //演员表演,观众等待-T;观众观看,演员等待-F
    String show;
    boolean flag = true;

    //表演
    public synchronized void play(String show){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了"+show);
        //通知观众观看
        this.notifyAll();
        this.show = show;
        this.flag = !flag;
    }

    //表演
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了"+show);
        //通知演员表演
        this.notifyAll();
        this.flag = !flag;
    }
}

控制台输出:

📢 可以看到表演和观看严格按照顺序来了!

2.4 线程池

📌 要点

  1. 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

  1. 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

  1. 好处

  • 提高响应速度(减少了创建新线程的时间)

  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)

  • 便于线程管理(corePoolSize:核心池的大小、maximumPoolSize:最大程数、keepAliveTime:线程没有任务时最多保持多长时间后会终止)

📌 线程池类

  1. JDK 5.0起提供了线程池相关API: ExecutorService 和 Executors

  1. ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

  • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable

  • <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable

  • void shutdown():关闭连接池

  1. Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

📌 简单举例

public class TestPool {
    public static void main(String[] args) {
        //1.创建线程池
        ExecutorService service = Executors.newFixedThreadPool(5);

        service.execute(new Mythread());
        service.execute(new Mythread());
        service.execute(new Mythread());
        service.execute(new Mythread());

        //2.关闭连接
        service.shutdown();
    }
}

class Mythread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 2; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

控制台输出:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/381245.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java知识复习(八)Spring基础

1、什么是Spring框架&#xff1f; Spring &#xff1a;是一款开源的轻量级 Java 开发框架&#xff0c;旨在提高开发人员的开发效率以及系统的可维护性 2、Spring、SpringMVC和SpringBoot的区别 Spring主要指Spring Framework&#xff0c;就是指如上图所示的各项功能模块Spr…

【Java 多线程学习】

多线程学习多线程1. 并行与并发2.进程和线程3. *****多线程的实现方式3.1 继承Thread类的方式进行实现3.2 实现Runnable接口方式进行实现3.3 利用Callable和Future接口方式实现3.4 设置获取线程名字4.获得线程对象5.线程休眠6.线程调度[线程的优先级]7.后台线程/守护线程多线程…

Pytest自动化框架-权威教程02-Pytest 使用及调用方法

Pytest 使用及调用方法使用python -m pytest调用pytest2.0版本新增你可以在命令行中通过Python编译器来调用Pytest执行测试:Copypython -m pytest [...]通过python调用会将当前目录也添加到sys.path中,除此之外,这几乎等同于命令行直接调用pytest [...]。可能出现的执行退出cod…

【K8S笔记】Kubernetes 集群架构与组件介绍

K8S 官方文档 https://kubernetes.io/zh/docs/home ##注重关注 概念和任务 板块。 K8S 集群架构 K8S也是运用了分布式集群架构&#xff1a; 管理节点/Master 整个集群的管理&#xff0c;任务协作。工作节点/Node 容器运行、删除。 K8S 组件介绍 管理节点/Master 相关组件 …

JDK1.8 ConcurrentHashMap

数据结构锁sizeCtlconcurrencyLevelForwardingNode、ReservationNode扩容get、put、removehashmap&#xff1a;线程不安全 hashtable&#xff1a;通过synchronized保证线程安全但效率低。强一致性 ConcurrentHashMap&#xff1a;弱一致性 数据结构 ConcurrentHashMap为node数…

前端转golang从小白到实战自学笔记(2023/3/1)

了解&#xff1a;https://www.runoob.com/go/go-concurrent.htmlgolang学习方向区块链研发工程师go服务器>&#xff08;特点&#xff1a;数据处理&#xff0c;处理大并发&#xff09;/游戏软件工程师golang分布式/云计算软件工程师&#xff08;盛大云、cdn、京东&#xff09…

Mybatis主要流程源码分析

分层架构图 主要流程图 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d9uhH9IK-1677658947721)(https://note.youdao.com/yws/public/resource/7f152b4b25320263d411a49583d3f4db/xmlnote/WEBRESOURCE9ea90840088eaeaa4a463bbc3f1912e8/17619)] 主…

索引优化与查询优化

1.哪些维度可以进行数据库调优 索引失效、没有充分利用到索引–》索引建立关联查询太多JOIN (设计缺陷或不得已的需求)–》SQL优化服务器调优及各个参数设置(缓冲、线程数等)–》调整my.cnf数据过多–》分库分表 大方向上完全可以分成 物理查询优化和 逻辑查询优化 两大块 物…

记录一次挖矿病毒kthreaddk和rcu_bj,导致CPU飙高处理

htop命令 存在kthreaddk和rcu_bj进程&#xff0c;cpu飙高 占用一般cpu或者70-80% 1、检查定时任务 查看是否有 # crontab -l 检查root账号是否有异常定时任务 有的话crontab -e 修改定时任务保存 并检查所有的用户有没有定时任务( 注&#xff1a;我的是gitlab git账户被入侵)异…

Nginx 配置详解(二)

序言Nginx的代理功能与负载均衡功能是最常被用到的&#xff0c;描述一些关于代理功能的配置&#xff0c;再说明负载均衡详细。Nginx 代理服务的配置说明1、设置 404 页面导向地址error_page 404 https://www.runnob.com; #错误页 proxy_intercept_errors on; #如果被代理服务…

PowerShell Install Mysql 5.7

MySQL介绍 MySQL 是最流行的关系型数据库管理系统&#xff0c;在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System&#xff1a;关系数据库管理系统)应用软件之一。 mysql download Mysql ServerdownloadPowershell 使用使用参数参考 前提条件 开启…

机器学习算法-KNN、决策树

目录1、最近邻算法 KNN1.1 K的选择1.2 案例&#xff1a;鸢尾花2、决策树2.1 决策树介绍2.2 案例&#xff1a;鸢尾花数据2.3 补充1、最近邻算法 KNN 原理&#xff1a;找到K 个与新数据最近的样本&#xff0c;取样本中最多的一个类别作为新数据的类别 要点&#xff1a;距离—是欧…

Three.js上手——搭建Vue3+Three.js项目

上一篇文章 Three.js初试 介绍了一些 Three.js 的基本概念&#xff0c;这一篇主要是介绍一下它的应用。 结合 Vue3 Vite 一起搭建一个项目。 项目初始化 Vite 项目构建 兼容性注意 Vite 需要 Node.js 版本 14.18&#xff0c;16。然而&#xff0c;有些模板需要依赖更高的 Nod…

代码随想录【Day29】|491. 递增子序列、46. 全排列、47. 全排列 II

491. 递增子序列 题目链接 题目描述&#xff1a; 给定一个整型数组, 你的任务是找到所有该数组的递增子序列&#xff0c;递增子序列的长度至少是2。 示例: 输入: [4, 6, 7, 7]输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]] 说明: …

教务选课排课系统

技术&#xff1a;Java、JSP等摘要&#xff1a;Internet网是目前全球最大的计算机通信网&#xff0c;它遍及全球几乎所有的国家和地区。www系统是一个大型的分布式超媒体信息数据库&#xff0c;它极大的推动了Internet的发展&#xff0c;己经成为Internet中最流行、最主要的信息…

防抖节流函数

防抖和节流对于每一个开发者来说&#xff0c;都不陌生。防抖和节流的概念其实最早并不是出现在软件工程中&#xff0c;防抖是出现在电子元件中&#xff0c;节流出现在流体流动中。 而JavaScript是事件驱动的&#xff0c;大量的操作会触发事件&#xff0c;加入到事件队列中处理…

骨传导耳机发声原理是什么,如何选购骨传导耳机

骨传导耳机很早以前就已经有了&#xff0c;但真正流行到运动圈里也还是最近两年的时间&#xff0c;所以导致很多人对骨传导耳机还不是很了解&#xff0c;不明白其工作原理是什么&#xff0c;在购买骨传导耳机时也总是会感到纠结&#xff0c;不知如何下手&#xff0c;作为一个多…

香港双重牌照、准入安排和禁止事项等重要制度已明确 20多万字的《虚拟资产咨询文件》以证雄心

前不久&#xff0c;香港证监会就加密货币交易发布的《有关适用于获证券及期货事务监察委员会发牌的虚拟资产交易平台营运者的建议监管规定的咨询文件》&#xff08;以下简称《咨询文件》&#xff09;&#xff0c;并如期就有关监管虚拟资产交易平台的建议展开咨询&#xff0c;以…

制造业数据质量提升的方法和实践

制造业的数据治理尚处于早期阶段&#xff0c;而数据质量管理是所有数据类项目重点关注的领域。数据治理以数据标准、数据质量和元数据的管理为根本&#xff0c;是企业实现数据资产价值创造的基础。上周&#xff0c;在由武汉市经济和信息化局主办的“万企育才工程”之《制造业数…

随机森林在sklearn中的实现

目录 一.集成算法 二.sklearn中的集成算法模块ensemble 三.RandomForestClassifier(随机森林分类器) 四.重要参数 1.基评估器参数 2.随机森林参数 五.重要属性和接口 六.Bagging的另一个必要条件 七.RandomForestRegressor(随机森林回归器) 八.机器学习中调参的基本思…