多线程案例(线程池篇)

线程池

线程池是什么

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

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

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

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

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

在这里插入图片描述

线程池存在的意义:减少用户态和内核态之间的交互,把更多的工作放到用户态去完成,通过这种方式提高程序效率
协程:是一种轻量级的用户态线程。简单来说,线程的调度是由操作系统负责,线程的睡眠、等待、唤醒的时机是由操作系统控制,开发者无法决定。使用协程,开发者可以自行控制程序切换的时机,可以在一个函数执行到一半的时候中断执行,让出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标准库中提供里线程池的实现,我们自己也可以写自己版本的实现

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...
win11本地账户怎么改名?win11很多操作都变了样,用户如果想要...