【JavaEE初阶--多线程初阶】实现一个线程池

文章目录

  • 1. 什么是线程池
    • 1.1 为什么线程放到池子中,就比从系统这边申请出来更快呢?
      • 1.1.1 用户态 vs 内核态
      • 创建线程,本身就需要内核的支持
  • 2. 标准库中的线程池
    • 有一个程序,在这个程序中要并发的/多线程的执行一些任务,如果使用线程池的话,那么这里的线程数要设为多少合适?
    • 我们搞多线程目的就是为了让程序跑的速度变快,但是为啥要考虑不让CPU占用率太高了呢?
    • 在我们的标准库中还提供了一个简化版本的线程池 Executors
  • 3. 实现一个线程池

1. 什么是线程池

老铁们可以在博主以前写的博客中了解到,所谓的进程比较重,如果频繁的创建和销毁,这样就会对内存资源的开销会很大,于是我们的解决办法:使用进程池或者线程。

线程,虽然比进程更轻了,但是如果创建销毁的频率进一步增加,仍然会发现内存资源的开销还是很大,那么我们的解决办法是:使用线程池或者协程。

所谓的线程池:就是把线程提前创建好,放到池子里。后面面程序中如果要使用到线程的话,就直接从池子里取,就不必从系统这边申请了,当线程使用完之后,也不是会给系统,而是放回池子里,以备下次使用。这样创建销毁线程,速度就更快了

1.1 为什么线程放到池子中,就比从系统这边申请出来更快呢?

1.1.1 用户态 vs 内核态

【JavaEE初阶--多线程初阶】实现一个线程池_第1张图片

创建线程,本身就需要内核的支持

创建贤臣的本质就是在内核中搞出一个PCB(任务运行控制块)加到链表中。在我们调用Thread.start()是,其实就跟接地也是要进入内核态来进行的。

而把创建好的线程放到"池子中",由于池子就是用户态实现的,那么这个放到池子/从池子起初,这个过程不需要这几到内核态,就是纯粹的用户态代码就能完成。

我们一般认为,纯用户态的操作,效率要比经过内核态处理的操作,效率要更高。,那么这又是为什么呢?

举一个例子:

【JavaEE初阶--多线程初阶】实现一个线程池_第2张图片

还有些老铁会说,我们如果创建线程池会不会浪费很多的空间?

我们创建线程是当然要有额外的空间开销,但是不能谁是"浪费"(用了,但是没有啥效果,就是浪费,用了有效果,就花得值)

2. 标准库中的线程池

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

让我们看看创建线程池的构造方法:

【JavaEE初阶--多线程初阶】实现一个线程池_第3张图片

有一个程序,在这个程序中要并发的/多线程的执行一些任务,如果使用线程池的话,那么这里的线程数要设为多少合适?

其实针对这个问题,在网上有很多的说法其实都是不正确的。

网上有这么一种典型的说法,假设机器有N核CPU,线程池的线程数目,就射为1 * N,1.2 * N ,1.5 * N ,2 * N…

但是只要能回答出一个具体的数字,那么都是错的

正确的做法:要通过性能擦拭的方式,找到合适的值

例如:写一个服务器程序,服务器里面通过线程池,多线程的处理请求,就可以对这个服务器进行性能测试。

比如构造一个请求,发送给服务器,要测试性能,这里的请求就需要构造很多。比如美妙发送500/1000个请求这样的场景,需要构造一个合适的值。

根据这里不同的线程池的线程数,来观察,程序处理任务的速度,程序持有的CPU的占有率。

当线程数多了,整体的速度就变快了,但是CPU占用率也就高了、

当线程少了,整体的速度是会变慢的,但是CPU占用率也会下降。因此我们就需要找一个程序速度能接受,并且CPU占用合理这样的平衡点。

不同类型的程序,因为单个任务里面CPU上计算的时间和阻塞的时间是分布不相同的,因此我们编一个数字往往是不靠谱的

我们搞多线程目的就是为了让程序跑的速度变快,但是为啥要考虑不让CPU占用率太高了呢?

对于线上服务器来说,要留一定的冗余。随时因对一些可能的突发情况(例如双十一等,请求就会暴涨),那么如果本身就把CPU块占满了,这时候突然来一波请求的废纸,此时服务器可能直接就挂了。

在我们的标准库中还提供了一个简化版本的线程池 Executors

这个简化版的线程池,本质就是针对ThreadPoolExceutor 进行了封装,还提供了一些默认的参数,

public class demo3 {
    public static void main(String[] args) {
        //创建一个线程池,在线程池中有10个线程
        ExecutorService pool = Executors.newFixedThreadPool(10);
        //Executors 创建线程的几种方式?
        //newFixedThreadPool:创建固定线程数的线程池
        //newCachedThreadPool:创建线程数目动态增长的线程池
        //newSingleThreadExecutor:创建只包含单个线程的线程池
        //newScheduledThreadPoll:设定延迟时间后执行命令,或者定期执行命令,是进阶版的Timer,具有定时器功能的线程池
        //在线程池中的线程中添加任务
        for(int i = 0;i<100;i++){
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello main");
                }
            });
        }
    }
}

3. 实现一个线程池

  • 先能够描述一个任务(直接使用Runnable)
  • 需要组织任务 (直接使用BlockQueue)
  • 能够描述工作线程
  • 还需要组织这些线程(使用List)
  • 还需要实现,往线程池里添加任务
 //自己实现一个线程池
class ThreadPool{
    //1.描述一个任务,直接使用runnable
    //2.使用阻塞队列,组织若干个任务
    //3.描述一个线程, 工作线程的功能就是从任务队列中取任务并执行.
    //4.把线程添加到线程池中。
    //5.设置submit()方法,向线程池中的线程中添加任务
    public BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
    static class worker extends Thread{
        BlockingQueue<Runnable> queue = null;

        public worker(BlockingQueue<Runnable> queue) {
            this.queue = queue;
        }
        //得到队列中的任务并且执行线程中的任务

        @Override
        public void run() {
             //得到线程池中的任务
            try {
                Runnable runnable = queue.take();
                runnable.run(); //执行任务
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //使用一个数据结构组织线程池中的众多线程
    public List<Runnable> list = new ArrayList<>();
    public ThreadPool(int n){
        for(int i = 0;i<n;i++){
             //得到每个线程
            worker worker = new worker(queue);
            list.add(worker);
        }
    }
    public void submit(Runnable runnable) throws InterruptedException {
        queue.put(runnable);
    }
}
public class demo4 {
    public static void main(String[] args) throws InterruptedException {
        ThreadPool pool = new ThreadPool(10);
        for(int i = 0;i<100;i++){
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("hello main");
                }
            });
        }
    }
}

你可能感兴趣的:(多线程,java-ee,java,开发语言)