[实例讲解]计算机处理任务的方法和原理--线程

news2024/11/19 17:21:48

[实例讲解]计算机处理任务的方法和原理

文章目录

    • [实例讲解]计算机处理任务的方法和原理
      • 情景一 所有的事情自己做
      • 情景二 找人去帮忙处理打印
      • 情景三 分别找人处理编码和打印
      • 情景四 不特定指定人去帮忙
      • 结束语

        在学习和工作中,我们自己都需要做很多的事情,事情大多都是我们亲自一件一件去完成,每件事情都是自己动手,那在做事情的花费时间就是每件事情时间的总和。但我们都梦想着有一天能当上领导,当有任务的时候,我们可以交给下属去做,我们直接获取结果就好,不用关注做事情的过程,这样就可以提高我们处理事情的效率。那这种情景在计算机中是如何模拟呢?

        这里我们就提出一个名词:线程

      在计算机中定义线程是计算机执行任务的基本的单位,即我们的任务可以交给线程去执行。

      在介绍线程之前,我们先介绍一下计算机中的任务执行:首先任务需放在内存,当得到调度的时候就需要分配CPU时间,有了CPU支持,任务才可以执行。常见的执行方式有两种:当我们采用一种串行的方式去执行任务,即CPU处理好当下的任务,才会去处理下一个任务,这样的执行时间就是每个任务时间的总和;当在性能要求较高的场景下,我们会采用一种并发的方式去执行任务,在每个线程执行任务时,会获得CPU时间片,在这一点时间内可以处理当前任务。在并发的方式中,每个任务都能通过一定的方式获取到一部份CPU时间。这样宏观下就可以看到多个任务在一起执行,而且并发也可以很好地利用CPU的性能。

串行:

在这里插入图片描述

并发:

在这里插入图片描述

        在讲解如何创建线程之前我们先建立两个方法:

        方法1:模拟打印的方法

/**
     * 打印方法
     */
public static void doPrint(){
    try{
        Thread.sleep(2000); // 模拟该方法处理时花费的时间
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println("--- doPrint ---");
}

        方法2:模拟我们敲代码的方法

/**
     * 编写代码方法
     */
public static void doCode(){
    try{
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println("--- doCode ---");

}

情景一 所有的事情自己做

        这种情景就是串行处理所有的事情,一件一件去处理,接下来以代码实现:

/**
     * 串行调用
     * @param args
     */
public static void main(String[] args) {
    long start = System.currentTimeMillis();
    doCode();
    doPrint();
    System.out.println(System.currentTimeMillis() - start);
}

        如代码所示,我们首先记录开始时间,然后串行去处理方法2和方法1

        执行的结果:

--- doCode ---
--- doPrint ---
5005

        如结果所示,串行执行方法的时间花费是两个方法的总和,这就相当于敲代码和打印这两件事自己来做,这种串行也是我们平时常见的编程方式,虽然很方便,但是并没有很好地利用计算机的性能,可能会导致无意义的等待。

情景二 找人去帮忙处理打印

        在这个情景中,我们采用一个线程去处理打印方法,模拟我们把一些事情提前给到其他人处理,自己只需等待结果处理完即可。接下来看看代码实现:

public static void main(String[] args) throws InterruptedException {
    long start = System.currentTimeMillis();
    // 建立一个线程,找个人去打印
    Thread threadA = new Thread(() -> {
        try {
            doPrint();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }, "threadA");
	// 启动一个线程
    threadA.start();

    doCode();
	// 阻塞等待线程结果
    threadA.join();
    // 打印花费的时间
    System.out.println(System.currentTimeMillis() - start);
}

        如上代码所示,使用Thread建立了一个线程threadA,让线程去处理打印方法,然后使用start()方法启动线程,最后使用join()等待线程threadA执行完毕。

        接下来看看执行的结果:

--- doPrint ---
--- doCode ---
3032

        如上结果所示,使用线程后,时间是doCode多一点点,这样使用线程后main线程就只需处理一个方法,执行效率得到了极大提升。

情景三 分别找人处理编码和打印

        在这种场景下,我们可以使用两个线程分别去处理两件事情,在main方法中我们就只需花费时间去获取结果就好。

代码如下:

// 找到两个人进行处理
public static void main(String[] args) throws InterruptedException {
    long start = System.currentTimeMillis();
    Thread threadA = new Thread(() -> {
        try {
            doPrint();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }, "threadA");

    Thread threadB = new Thread(() -> {
        try {
            doCode();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }, "threadB");
    // 启动线程
    threadA.start();
    threadB.start();
	// 阻塞当前线程,等待两个线程运行结束
    threadA.join();
    threadB.join();

    System.out.println(System.currentTimeMillis() - start);
}

        如上所示,采用了两个相处去处理编码和打印两个方法。

        运行结果:

--- doPrint ---
--- doCode ---
3047

        大家可能有疑惑怎么时间和情景二结果一致呢?这是因为在Main方法(线程)中我们需要等待两个线程的运行结果,在代码中我们使用到了join()方法,这个方法是一种同步阻塞的方法,即他会阻塞Main线程,等待threadAthreadB两个线程运行结束。又因为是用了两个线程,所以等待的时间就是较长方法的时间。

情景四 不特定指定人去帮忙

        在上面的几个情景中,我们都显示去创建了每个线程;其实线程的创建和销毁在计算机中是比较的耗资源的,因此为了解决每次的线程创建和销毁,我们都会使用线程池来处理任务,即在线程中会一直存在一些已经创建的线程,每次有任务就直接分配CPU时间片唤醒线程就可以了。接下来我们就来使用线程池来处理我们的两个方法。

        创建一个线程池

private final static int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();

private final static ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(AVAILABLE_PROCESSORS, AVAILABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<>(5), new ThreadPoolExecutor.CallerRunsPolicy());

        如上我们就建立了一个线程池POOL_EXECUTOR啦,其中AVAILABLE_PROCESSORS是为了获取计算机的核心数量;然后使用了ThreadPoolExector显示创建了一个线程池POOL_EXECUTOR

其中ThreadPoolExector中的参数分别为:

  • AVAILABLE_PROCESSORS:核心线程数量,在线程池中会一直存在的线程。

  • AVAILABLE_PROCESSORS * 2:最大的线程数量。

  • 1, TimeUnit.MINUTES:空闲时间为1分钟,即当过多的线程执行完任务后,会等待1分钟,如果没有任务执行就会销毁掉多余的线程。

  • new LinkedBlockingDeque<>(5): 这个是一个任务队列,当创建的线程数量大于最大的线程数时,就会把多余的线程添加到任务队列中。

  • new ThreadPoolExecutor.CallerRunsPolicy():当任务队列都满了时,就会执行拒绝策略,将新加的任务给抛弃掉。

        然后将编码和打印的任务都交给线程池来执行:

public static void main(String[] args) throws InterruptedException {
    long start = System.currentTimeMillis();
    POOL_EXECUTOR.execute(() -> {
        try {
            doPrint();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });

    POOL_EXECUTOR.execute(() -> {
        try {
            doCode();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });
    System.out.println(System.currentTimeMillis() - start);
    // 挂起当前线程
    Thread.currentThread().join();
}

        运行结果:

63
--- doPrint ---
--- doCode ---

        如结果所示,使用了线程池,执行了我们的方法,并且main方法等待的时间大大缩小了,但是他的执行顺序是排到第一的。这其实是因为我们没有阻塞去等待其他的方法,所有在main线程中,先打印了时间,这也是并发的原因,线程的执行顺序是不能保证的。

那如何阻塞线程池的任务?

//修改doPrint和doCode两个方法,添加返回值。
/**
   * 打印方法
   */
public static String doPrint1(){
    try{
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println("--- doPrint ---");
    return "Print Task Done";
}

/**
   * 编写代码方法
   */
public static String doCode1(){
    try{
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println("--- doCode ---");
    return "Code Task Done";

}
public static void main(String[] args) throws ExecutionException, InterruptedException {

    long start = System.currentTimeMillis();

    Future<?> doPrint = POOL_EXECUTOR.submit(() -> {
        try {
            doPrint1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    });

    Future<?> doCode = POOL_EXECUTOR.submit(() -> {
        try {
            doCode1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    });

    // 阻塞线程
    doPrint.get();
    doCode.get();

    System.out.println(System.currentTimeMillis() - start);
}

运行结果:

--- doPrint ---
--- doCode ---
3051

如上所述,当线程使用execute()方法时,是不需要阻塞去获取结果的,这种可以使用到日志打印。使用submit()就可以使用get()方法去阻塞获取结果。

结束语

        在实际的开发中,使用线程去实现异步执行的地方很多,但我们都常用线程池来实现并发处理的,能提高效率,也提高可靠性。欢迎大家来公众号沟通,谢谢!

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

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

相关文章

【机器学习】匈牙利和KM匹配个人理解

基础知识 二分图 【定义】图论中的一种特殊模型。若能将无向图G(V,E)的顶点V划分为两个交集为空的顶点集&#xff0c;并且任意边的两个端点都分属于两个集合&#xff0c;则称图G为一个为二分图。 【解释】一张图要是二分图&#xff0c;需要满足以下几个要求&#xff1a; &a…

PS 矩形选区工具(2)模式快捷键 选区比例调整 颜色填充

本文为PS 矩形选区工具(1)基本用法 生成图层 选区方式演示讲解的续文 拉出选区后 用鼠标拖动选区 是可以拖动选区位置的 拉出一块选区是 按住键盘 Shift键 就可以用鼠标再拖出一块选区 加选区快捷键 按住 Shift 减选区 按住键盘 Alt 键 用鼠标拖出不要的选区位置即可 减选…

新认定金山区企业技术中心给予一次性奖励15万元

金山区企业技术中心一、主管部门金山区经济委员会二、政策依据《金山区关于深化建设打响“上海制造”品牌的重要承载区促进实体经济高质量发展的若干政策》&#xff08;金府发〔2019〕8号&#xff09;《印发<关于深化建设打响“上海制造”品牌的重要承载区促进实体经济高质量…

衣服、商品、商城网站模板首页,仿U袋网,vue+elementui简洁实现(三)

一.以往版本回顾 作者成品效果访问&#xff1a;点击访问 官方详情页访问&#xff1a;点击访问 版本1《衣服、商品、商城网站模板首页&#xff0c;仿U袋网&#xff0c;vueelementui简洁实现》版本2《衣服、商品、商城网站模板首页&#xff0c;仿U袋网&#xff0c;vueelementui…

【微服务】springboot 实现elasticsearch索引数据迁移

一、前言 在生产系统中&#xff0c;经常可能面临的一个状况就是&#xff0c;随着实际业务发生变更&#xff0c;现有的数据模型可能需要调整&#xff0c;而且到了必须调整不可的时候&#xff0c;那就只能硬着头皮做了&#xff1b; 数据模型的调整&#xff0c;说的大一点&#xf…

flask 和 echarts 使用柱状图折线图等可视化图表展示二手房统计数据

目录 一、实战场景 二、知识点 python 基础语法 python 文件读写 pandas 数据处理 flask web 框架 echarts 图表 jinja 模版 三、菜鸟实战 初始化 Flask 框架&#xff0c;设置路由 各行政区房屋均价柱状图分析 echarts 渲染柱状图 各面积区间房屋占比饼状图 echa…

逻辑漏洞渗透与攻防(六)之其他类型逻辑漏洞

目录 其他类型逻辑漏洞 数据包重放漏洞 条件竞争漏洞 订单金额任意修改 接口无限制枚举 支付漏洞 修改商品数量 修改支付状态 修改附属值 越权支付 无限制试用 支付漏洞总结 SRC中的逻辑漏洞总结 其他类型逻辑漏洞 数据包重放漏洞 漏洞介绍&#xff1a;通…

SpringBoot下RabbitMQ的实战应用:动态创建和动态监控队列、死信、备份交换机

一、应用场景 业务中心根据业务需求向特定用户发送消息&#xff1b;发送前不确定由哪个用户接收 特定用户接收特定消息&#xff1b;用户可以退出&#xff0c;再切换别的用户登录&#xff0c;用户登录后只接收与自已对应的消息 二、总体要求 项目要足够稳健&#xff0c;消息不能…

(3分钟了解)SLAM后端优化的四大金刚!g2o ceres gtsam SE-Sync

后端优化常用的库有g2o ceres gtsam 和 se-sync这篇博客首先介绍se-sync&#xff0c;然后比较四种库之间的差异。编辑切换为居中添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;编辑切换为居中添加图片注释&#xff0c;不超过 140 字&#xff08;可选&…

Python实现检测图片中的人脸,将识别到的人脸向量存入数据库,并实现提交的图片的人脸信息与入库的人脸信息进行比对

facenet_face_regonistant 完整代码下载地址&#xff1a;Python实现检测图片中的人脸&#xff0c;将识别到的人脸向量存入数据库 利用facenet实现检测图片中的人脸&#xff0c;将识别到的人脸向量存入数据库&#xff0c;此外利用post提交一个新图片&#xff08;也可以提交一个…

【Qt】通过继承ui界面类的方式加载.ui转化的.h头文件显示窗体

【Qt】通过继承ui界面类的方式加载.ui转化的.h头文件显示窗体1、背景2、实例3、验证1、背景 将.ui文件转化为.h头文件参考如下博客&#xff1a; 【Qt】将QtDesigner生成的.ui文件转化为.h头文件 https://jn10010537.blog.csdn.net/article/details/128589666其中生成的ui_wid…

Web学习

Web概念JavaWeb&#xff1a;使用Java语言开发基于互联网小贷项目软件架构&#xff1a;B/S架构详解静态资源HTML举例标签学习文件标签<!--注释-->文本标签图片标签* img&#xff1a;展示图片* 属性&#xff1a;* src&#xff1a;指定图片的位置* 代码&#xff1a;<!--展…

你的PC机或者终端,是怎么上网的?怎么连接到网络的?无线网怎么接入Internet,如何访问网络资源?

你的PC机或者终端&#xff0c;是怎么上网的&#xff1f;怎么连接到网络的&#xff1f;无线网怎么接入Internet&#xff0c;如何访问网络资源&#xff1f; 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都…

就该去造空调吗?

今天在知乎上收到一个网友的提问&#xff1a;大佬&#xff0c;我想咨询点人生问题&#xff0c;但是碍于没工作囊中羞涩&#xff0c;实在没钱问。希望能问一问人生选择问题 是这样的一个是奥克斯空调电控部&#xff0c;一个是大陆汽车电子重庆研发&#xff0c;奥克斯偏裸机&…

光耦合器:其类型和在DC/AC电路中的各种应用

光耦合器&#xff1a;其类型和在DC/AC电路中的各种应用 介绍 光耦合器是一种在两个隔离电路之间传输电信号的电子元件。光耦合器也称为光隔离器、光电耦合器或光隔离器。 光耦合器通常用于电路&#xff0c;尤其是低电压或高噪声敏感电路&#xff0c;用于隔离电路&#xff0c…

【C++常用算法】STL基础语法学习 | 排序算法

目录 ●sort ●random_shuffle ● merge ●reverse ●sort 1.功能描述&#xff1a; 对容器内元素进行排序 2.查看sort定义下底层代码的函数原型&#xff1a; 3.向vector容器中插入10个无序数&#xff0c;并且用sort排序法对其进行升序和降序&#xff08;内建仿函数greater<…

伦敦交通局在这里为您的无障碍旅程提供支持

伦敦交通局首席人事官特里西亚赖特&#xff08;Tricia Wright&#xff09;详细介绍了伦敦交通局努力使该市的交通网络更易于所有乘客使用的方式&#xff0c;并强调尽管已经做了很多工作&#xff0c;但这项工作只是一个开始。公共交通是伦敦人和游客在首都生活中必不可少的。它连…

c++写一个连接池

用c写一个数据库连接池 数据库连接池是为了提高数据库连接的性能&#xff0c;进行连接复用 对于复杂数据库进行大量引用的场景下就会出现访问瓶颈 常见的两种解决方法就是&#xff1a;为了减少磁盘 I/O的次数&#xff0c;在数据库和服务器的应用中间加一层 缓存数据库&#…

2023/1/7 Vue学习笔记-4-组件的理解

1 对组件的理解 模块与组件、模块化与组件化&#xff1a; 1.模块&#xff1a; &#xff08;1&#xff09;理解&#xff1a;向外提供特定功能的js程序&#xff0c;一般就是一个js文件 &#xff08;2&#xff09;为什么&#xff1a;js文件很多很复杂 &#xff08;3&#xff09;作…

元编程:constexpr +特例化 判断质数

重点&#xff1a; 1.constexpr 函数支持在编译期间完成计算 2.特例化是模板中一种定义 using namespace std;//编译期进行判断 constexpr bool isPrime(unsigned int p) {for (unsigned int d2;d<p/2;d){if (p % d 0){return false;}}return p > 1; }template<int…