JavaEE初阶 -多线程基础篇 (线程池)

为什么要使用线程池?
Java标准库中的线程池
实现一个线程池


1. 为什么要使用线程池?

  提前将线程创建好, 存在线程池中, 后面如果要使用线程可以直接从线程池中获取, 而不必从系统中申请, 线程结束之后也不是由系统销毁, 而是继续存入线程池中, 这样, 线程创建和销毁的速度会加快.

那么, 为什么在线程池里取出线程比系统创建线程要快呢?

  操作系统中存在两种状态, 用户态内核态, 程序中的部分指令需要调用操作系统的API, 进一步的逻辑都会在内核中执行, 这种代码就是内核态的代码.

  创建线程本身就需要内核的支持(创建线程就是在内核中创建一个PCB, 并将PCB串在链表上), 包括开启线程(Thread.start)也需要进入内核态来进行.

  而将创建好的线程加入线程池中, 由于线程池是在用户态下实现的, 在线程池中存放/取出线程, 这个过程不需要涉及到内核态.

  一般认为, 纯用户态的操作, 效率高于经过内核态处理的操作. 纯用户态的操作是可控的, 而内核态的操作整体是不可控的, 我们这里提到的内核态操作的效率更低, 指的是内核态的执行过程是不可控的, 内核态有可能在执行这条指令的时候也执行了其他的指令, 所以说纯用户态的操作效率高于经过内核态的操作.

2. Java标准库中的线程池

  Java中描述线程池的类叫做"ThreadPoolExecutor", 这个类位于java.util.concurrent包中, 共有四种构造方法:

  1. ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
    创建一个新的 ThreadPoolExecutor, 给定初始参数和默认线程工厂和拒绝执行处理程序。
  2. ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue,
    RejectedExecutionHandler handler) 创建一个新的
    ThreadPoolExecutor与给定的初始参数和默认线程工厂。
  3. ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue,
    ThreadFactory threadFactory) 创建一个新的
    ThreadPoolExecutor, 给定的初始参数和默认拒绝执行处理程序。
  4. ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue,
    ThreadFactory threadFactory, RejectedExecutionHandler handler) 创建一个新
    ThreadPoolExecutor, 给定初始参数。

看起来有点难理解, 我们只需要关注第四个构造方法中的参数即可:

  1. int corePoolSize:核心线程数

  2. int maximumPoolSize:最大线程数

  3. long keepAliveTime:非核心线程等待的最大时间

  4. TimeUnit unit:时间单位(s, ms, us)

  5. BlockingQueue workqueue:任务队列(线程池会提供一个submit()方法, 将任务注册到线程池中, 也就是加入这个任务队列中)

  6. ThreadFactory threadFactory:线程工厂(线程的创建)

  7. RejectedExecutionHandler handler:拒绝策略(线程池满了之后应该怎么做)

线程池的参数非常多, 其中最重要的是线程的个数问题.

有一个程序要并发地去执行一些任务, 如果使用线程的话, 如何设置线程池的大小?

  通过性能测试找到一个合适的值, 例如:写一个服务器程序, 服务器里通过线程池, 多线程的处理用户请求, 就可以对这个服务器进行性能测试, 比如构造一些请求, 发送给服务器, 要测试性能, 这里的请求就要构造很多, 然后根据实际的业务场景, 构造合适的请求个数

  根据不同的线程数, 来观察持有的CPU的占用率, 从而找到一个速度能被用户接受, CPU占用率也合理的平衡点(对于线上服务器来说, 为了应对一些突发情况, 需要CPU留有一定的冗余, 因此CPU的占用率不能过高)

标准库中还提供了一个简化版的线程池 - Executors, 本质是对ThreadPoolExecutor进行封装, 提供一些默认参数.

public static void main(String[] args) {
    //创建一个固定大小的线程池, 参数决定了线程池中线程的最大个数
    ExecutorService pool = Executors.newFixedThreadPool(10);
    //创建一个自动扩容的线程池
    Executors.newCachedThreadPool();
    //创建一个只有一个线程的线程池
    Executors.newSingleThreadExecutor();
    //创建一个具有定时器功能的线程池
    Executors.newScheduledThreadPool(3000);
    pool.submit(new Runnable() {
        @Override
        public void run() {
           //执行的任务
        }
    });
}

3. 实现一个线程池

实现线程池需要:

  1. 描述任务(使用Runnable接口)
  2. 组织任务(使用BlockingQueue阻塞队列)
  3. 描述工作线程
  4. 组织线程
  5. 创建一个方法, 允许线程入池

具体实现代码:

class MyThreadPool{
    //1. 使用Runnable描述任务
    //2. 使用阻塞队列组织任务
    private BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>();
    //3. 描述一个线程
    static class Worker extends Thread{
        private BlockingQueue<Runnable> blockingQueue = null;
        public Worker(BlockingQueue<Runnable> blockingQueue){
            this.blockingQueue = blockingQueue;
        }

        @Override
        public void run() {
            while (true){
                try {
                    //循环地获取任务队列中的任务
                    //如果队列不为空, 获取到队首元素, 队列为空则产生阻塞
                    Runnable runnable = blockingQueue.take();
                    //执行任务
                    runnable.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //4. 组织若干个线程
    private List<Thread> workers = new ArrayList<>();

    //在构造方法中创建若干个线程放到上面的顺序表中, n代表线程的个数
    public MyThreadPool(int n) {
        for(int i=0;i<n;++i){
            Worker worker = new Worker(blockingQueue);
            worker.start();
            workers.add(worker);
        }
    }
    //5. 创建一个方法, 允许线程进入线程池
    public void submit(Runnable runnable){
        try {
            blockingQueue.put(runnable);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhanglf6699

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值