欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java并发——阿里架构师是如何巧用线程池的!

程序员文章站 2024-01-20 10:02:46
一、创建线程 1.创建普通对象,只是在JVM的堆里分配一块内存而已 2.创建线程,需要调用操作系统内核的API,然后操作系统需要为线程分配一系列资源,成本很高 线程是一个重量级对象,应该避免频繁创建和销毁,采用线程池方案 二、一般的池化资源 三、生产者-消费者模式 业界线程池的设计,普遍采用生产者- ......

一、创建线程

1.创建普通对象,只是在jvm的堆里分配一块内存而已

2.创建线程,需要调用操作系统内核的api,然后操作系统需要为线程分配一系列资源,成本很高

  • 线程是一个重量级对象,应该避免频繁创建和销毁,采用线程池方案

二、一般的池化资源

// 假设java线程池采用一般意义上池化资源的设计方法
class threadpool {
    // 获取空闲线程
    thread acquire() {
    }
    // 释放线程
    void release(thread t) {
    }
}
// 期望的使用
threadpool pool;
thread t1 = pool.acquire();
// 传入runnable对象
t1.execute(() -> {
    // 具体业务逻辑
});

 

三、生产者-消费者模式

业界线程池的设计,普遍采用生产者-消费者模式,线程池的使用方是生产者,线程池本身是消费者

public class mythreadpool {
    // 工作线程负责消费任务并执行任务
    class workerthread extends thread {
        @override
        public void run() {
            // 循环取任务并执行
            while (true) {
                runnable task = null;
                try {
                    task = workqueue.take();
                } catch (interruptedexception e) {
                }
                task.run();
            }
        }
    }

    // 利用阻塞队列实现生产者-消费者模式
    private blockingqueue<runnable> workqueue;
    // 内部保存工作线程
    list<workerthread> threads = new arraylist<>();

    public mythreadpool(int poolsize, blockingqueue<runnable> workqueue) {
        this.workqueue = workqueue;
        for (int i = 0; i < poolsize; i++) {
            workerthread work = new workerthread();
            work.start();
            threads.add(work);
        }
    }

    // 提交任务
    public void execute(runnable command) throws interruptedexception {
        workqueue.put(command);
    }

    public static void main(string[] args) throws interruptedexception {
        // 创建有界阻塞队列
        blockingqueue<runnable> workqueue = new linkedblockingqueue<>(2);
        // 创建线程池
        mythreadpool pool = new mythreadpool(10, workqueue);
        // 提交任务
        pool.execute(() -> {
            system.out.println("hello");
        });
    }
}

 

四、java线程池

ⅰ. threadpoolexecutor

public threadpoolexecutor(int corepoolsize,
                          int maximumpoolsize,
                          long keepalivetime,
                          timeunit unit,
                          blockingqueue<runnable> workqueue,
                          threadfactory threadfactory,
                          rejectedexecutionhandler handler)

// 让所有线程都支持超时,如果线程池很闲,那么将撤销所有线程
public void allowcorethreadtimeout(boolean value)

1.corepoolsize:线程池保有的最小线程数

2.maximumpoolsize:线程池创建的最大线程数

3.keepalivetime & unit

  • 如果一个线程空闲了keepalivetime & unit,并且线程池的线程数大于corepoolsize,那么这个空闲的线程就要被回收

4.workqueue:工作队列

5.threadfactory:自定义如何创建线程

6.handler

  • 线程池中的所有线程都很忙碌,并且工作队列也满了(工作队列是有界队列),此时提交任务,线程池会拒绝接收
  • callerrunspolicy:提交任务的线程自己去执行该任务
  • abortpolicy:默认的拒绝策略,抛出rejectedexecutionexception
  • discardpolicy:直接丢弃任务,不会抛出任何异常
  • discardoldestpolicy:丢弃最老的任务,然后把新任务加入到工作队列中

ⅱ. executors

1.不建议使用executors,因为executors提供的很多默认方法使用的是*队列linkedblockingqueue

2.在高负载的情况下,*队列容易导致oom,而oom会导致所有请求都无法处理

3.因此强烈建议使用有界队列

ⅲ. 拒绝策略

1.使用有界队列,当任务过多时,线程池会触发拒绝策略

2.线程池默认的拒绝策略会抛出rejectedexecutionexception,这是一个运行时异常,开发时很容易忽略

3.如果线程池处理的任务非常重要,可以自定义拒绝策略

ⅳ. 异常处理

1.使用threadpoolexecutor.execute()方法提交任务时,如果任务在执行过程中出现运行时异常

  • 会导致执行任务的线程终止,并且无法获得任何通知

2.因此最稳妥的方法还是捕获所有异常并处理

try {
    // 业务逻辑
} catch (runtimeexception x) {
    // 按需处理
} catch (throwable x) {
    // 按需处理
}

 

写在最后

Java并发——阿里架构师是如何巧用线程池的!