多线程案例(线程池篇)

线程池

线程池是什么

进程本身已经能够做到并发编程,但由于进程太"重量"了,创建和销毁的成本比较高(需要申请和释放资源),线程可以看作是轻量级的进程(共用一组系统资源)。虽然如此,但是在更频繁的创建和释放的情况下,线程也抗不住了,因此要做进一步的优化,其中有两种方式:线程池和协程(纤程)
协程:轻量级线程

线程池就是把线程创建好之后,放到池子里,需要使用线程,就直接从池子里取,而不是通过系统来创建,当线程用完了,就将线程还到池子里,而不是通过系统来销毁,通过线程池的方法来进一步提高效率

【为什么将线程放到池子里,从池子里取线程就要比从系统创建线程更高效呢?】

从池子里取线程,属于纯用户态操作,通过系统来创建线程,设及内核态操作,通常认为,牵扯到内核态的操作,就要比纯用户态的操作更低效

【为什么用户态操作比内核态操作更高效呢?】

在这里插入图片描述

线程池存在的意义:减少用户态和内核态之间的交互,把更多的工作放到用户态去完成,通过这种方式提高程序效率
协程:是一种轻量级的用户态线程。简单来说,线程的调度是由操作系统负责,线程的睡眠、等待、唤醒的时机是由操作系统控制,开发者无法决定。使用协程,开发者可以自行控制程序切换的时机,可以在一个函数执行到一半的时候中断执行,让出CPU,在需要的时候再回到中断点继续执行。因为切换的时机是由开发者来决定的,就可以结合业务的需求来实现一些高级的特性。

标准库中的线程池

  • 使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
  • 返回值类型为 ExecutorService
  • 通过 ExecutorService.submit 可以注册一个任务到线程池中.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class threadpool {
    public static void main(String[] args) {
        ExecutorService pool= Executors.newFixedThreadPool(10);
        for(int i=0;i<100;i++){
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello pool");
                }
            });
        }
    }
}

执行结果:

在这里插入图片描述

虽然我们创建的线程池中只有10个线程,而我们线程池中submit了100个任务 因为线程池中的线程执行完一个任务后,可以接着执行下一个任务

Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.

Executors里面的各种工厂方法,其实都是针对ThreadPoolExecutor这个类的进行了new,并且传入了不同风格的参数,来达到构造不同种类线程池的目的

工厂方法及工厂模式

newFixedThreadPool是Executors类的静态方法,借助静态方法来创建实例,像这样的方法,称为"工厂方法",对应的设计模式就叫做"工厂模式".

通常情况下,创建对象借助new关键字,调用构造方法来实现的,但是在C++/Java中的构造方法,有很多的限制,在很多时候不方便使用,因此就需要给构造方法在包装一层,外面起到包装作用的方法就是工厂方法

构造方法受到的限制,比如构造方法的名字必须和类名相同,如果想要实现不同版本的构造,就需要重载,而重载又要受到限制(参数类型,个数,顺序不同)

在这里插入图片描述


通过工厂模式解决上述问题:

//表示一个点
class Point{
    double x;
    double y;
    double r;
    double a;


    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }

    public void setR(double r) {
        this.r = r;
    }
    public void setA(double a) {
        this.a = a;
    }

    public static Point makePointByXY(double x, double y){
       Point point=new Point();
      point.setX(x);
      point.setY(y);
      return point; 
    }
    public static  Point makePointByRA(double r,double a){
        Point point=new Point();
        point.setR(r);
        point.setA(a);
        return point;
    }
}

实现线程池

  • 简单实现成可指定线程个数的线程池
  • 核心操作为 submit, 将任务加入线程池中
  • 使用 Runnable 描述一个任务
  • 使用一个 BlockingQueue 组织所有的任务
  • 每个 worker 线程要做的事情: 不停的从 BlockingQueue 中取任务并执行.
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class MyThreadPool {
  private BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();

  public void submit(Runnable runnable) throws InterruptedException {
   queue.put(runnable);
  }
  //在构造方法中创建线程,让这些线程来执行任务
 public MyThreadPool(int n){

 for(int i=0;i<n;i++){
    Thread worker=new Thread(()->{
        while(!Thread.currentThread().isInterrupted()){
            try {
                Runnable runnable = queue.take();
                runnable.run();
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    });
  worker.start();
 }
 }

    public static void main(String[] args) throws InterruptedException {
        MyThreadPool pool=new MyThreadPool(10);
        for (int i = 0; i < 30; i++) {
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello MyPool");
                }
            });
        }
    }
}

执行结果:

在这里插入图片描述


【补充】:

线程池和字符串常量池都是广义的概念
Java里面JVM中实现了字符串常量池,我们在自己写的Java业务代码中,也能实现自己版本的字符串常量池
线程池在Java标准库中提供里线程池的实现,我们自己也可以写自己版本的实现

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340