• 【构建并发程序】1-线程池-Executor-ExecutionContext


    创建线程的缺点

    • 1-创建线程的代价比为对象赋值、获取监控器锁和更新集合中的某个条目等操作所要花费的代价高的多
    • 2-如果应用程序需要执行大量的小型并发任务,并需要较高的吞吐量,那么我们就负担不起为每个任务,都创建一个新线程的花费。
    • 3-在创建线程时我们需要为该线程的调用栈,分配一块内存区域,并在线程之间进行上下文切换,这样做所花费的时间比以并发方式完成任务所花的时间还要多。

    什么是线程池?

    • 在每秒数千个请求的情况下,为每个请求都要创建一个新线程会拖慢系统的运行速度,因此同一个线程应该可以能够让许多个请求反复来进行使用;这些可重用的线程通常被称为线程池。

    什么是Executor?

    Executor如何创建的线程池?

    • Executor是一个简单的接口,它定义了一个名为execute的方法,该方法接受Runnable对象,并且最终会调用该对象中的run方法。
    • 通常Executor接口会以并发的方式将Runnable对象,处理为调用execute方 法的线程,然后将这些线程实现为 “线程池”;

    Scala|Java中如何使用executor?

    • ForkJoinPool是在JDK7中引入的一个Executor接口。通过导入scala.concurrent.forkjoin软件包,Scala程序也能够使用该接口。

    代码案例

    • 下面的代码展示了实例化ForkJoinPool接口的方式,
    • 以及对其提交能够通过,异步处理任务的方式
    object one_Executor extends App {
      /**
       * 下面的代码展示了实例化ForkJoinPool接口的方式,
       * 以及对其提交能够通过,异步处理任务的方式
       * */
    
      import scala.concurrent._
    
      val executor = new forkjoin.ForkJoinPool()
      executor.execute(new Runnable {
        override def run(): Unit = println("This task is run asynchronously")
      })
    
      /**
       * 这里加一个sleep的原因是?
       * 用以防止ForkJoinPool对象中的 “守护线程” 在调用Runnable对象中的run方法之前被终止。
       * (默认情况下ForkJoinPool对象创建的线程都是守护线程)
       * */
      //  Thread.sleep(500) //有了awaitTermination就不用在用sleep了
      executor.shutdown() //调用shutdown时,不能再往线程池中添加任何任务,否则将会抛出RejectedExecutionException异常
    
      //awaitTermination是一个阻塞方法。它必须等待线程池(ForkJoinPool(Executor))“退出”后才会结束自身。(shutdown放在其后面根本不会运行,因为都已经堵塞了)
      // 1、因为是守护线程,main线程没退出,守护线程也不会“退出”,所以就在这堵塞了
      // 2、shutdown放在其前面,awaitTermination等待期间,shutdown也在等待任务的结束。
      // 等待期间,任务结束了shutdown才会关闭线程并“退出”,awaitTermination发现shutdown退出,则表示任务结束,此时会返回true。
      // 等待期间,awaitTermination发现shutdown没有退出,则表示任务没结束,此时会返回false。
      val b: Boolean = executor.awaitTermination(2, TimeUnit.SECONDS)
      println(b) //true
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    ExecutionContext

    • 创建ExecutionContext线程池的2种办法:
    • 1-通过全局对象创建(默认时8个线程)
      • object ceshi extends App{
        	val a = ExecutionContext.global
        	a.execute(new Runnable{
        		def run()=log.info("创建")
        	})
        	Thread.sleep(500)
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
    • 2-通过ForkJoinPool创建(这里创建了2个线程)
      •  object ceshi extends App{
         	val pool = new forkjoin.ForkJoinPool(2)
         	val a = ExecutionContext.fromExecutorService(pool)
         	a.execute(new Runnable{
         		def run()=log.info("创建")
         	})
         	Thread.sleep(500)
         }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8

    注意:
     ExecutionContext类的伴生对象,定义了2个方法:

    •  fromExecutor:里面没有shutdown
    •  fromExecutorService:扩展了fromExecutor,里面旧有shutdown

    (本文章虽然采用的代码为scala代码,但java代码与Scala代码可以互相转换,且本质上两者所阐述的东西都是一致的)

  • 相关阅读:
    基于Matlab2012a的LineStretcher测线编号程序开发
    新手教师如何迅速成长
    从有序顺序表中删除所有值重复的元素,使表中所有元素的值均不同
    【Element-plus】如何让滚动条永远在最底部(支持在线演示)
    【2023年11月第四版教材】软考高项极限冲刺篇笔记(2)
    MySql配置环境变量及修改密码
    [STM32学习]定时器实现码表(数码管显示)
    Java虚拟机(JVM)
    Ubuntu22.04 | 使用教程
    大型网站系统架构演化实例_7.使用NoSQL和搜索引擎
  • 原文地址:https://blog.csdn.net/qq_33982605/article/details/126669606