用在哪:多线程用来干啥的
- 一个任务分为多个子任务,并行执行,提高该任务执行效率,比如要给目标列表推送消息,可以用多个线程来并行发消息。
- 在主线程中异步执行其他任务,避免串行执行的等待。比如处理完交易后的发消息通知,可以提交发通知任务给线程池进行异步处理,减少主任务响应时间
怎么用:怎么使用多线程
- 实现Runnable或者Callable接口(需要获取任务的返回值时使用)来定义任务job,一般不使用继承Thread类,实际使用多半是定义任务将其提交线程池来执行。一般在demo测试api时使用继承Thread
- 将job提交给线程池来执行, 线程池创建主要以下几种方式
- 使用ThreadPoolExecutor:
ThreadPoolExecutor executor=new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
- 使用Executors:
#按需创建线程的执行器
ExecutorService executor=Executors.newCachedThreadPool() ;
#单一线程执行器
ExecutorService executor=Executors.newSingleThreadExecutor() ;
#固定大小的执行器
ExecutorService executor=Executors.newFixedThreadPool(int nThreads);
#可调度的执行器
ExecutorService executor=Executors.newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) ;
将任务提交给线程池执行 exector.submit(job)。当是callable 任务时可以获取返回Future 或者ScheduledFuture用来查询任务执行状态和执行结果
也可以使用spring自带的异步任务。在配置类上注解@EnableAsync 开启spring的异步任务,在需要异步执行的方法上用@Async即可异步执行任务。
为什么:为什么要用多线程
- 常见web场景都是cpu运算较快,cpu经常大量时间用在等待io上等待(比如网络io,文件io),使用多线程,减少cpu的等待io时间,充分利用cpu的计算性能。
注意事项
多线程安全:多线程并发访问共享资源会存在线程安全问题,将在下一篇讲到,因此在多线程编程特别注意共享资源的使用。
尽量避免继承Thread类创建线程,多使用线程池,减少频繁创建和销毁线程带来的开销。
线程的数量应该看需求设置合理的数值。避免线程设置过大或者过小。过大将带来很大的上下文切换开销,消耗更多内存。过小则浪费cpu。一般推荐:Nthread = Ncpu * Ucpu *(1+W/C),各字段含义:Nthreads:线程数量;Ncpu:CPU的数量;Ucpu:CPU使用率,范围在[0,1];W/C:等待时间与计算时间的比率
分清是计算密集型还是IO密集型,如果是C无限大也就是计算密集型的那么线程太多意义不大,因为需要CPU计算,起多了也没用,一般n+1。如果是IO密集型那么可以起更多的线程,因为等待时间过多,一般2n+1或者3n+1
Stream的parallelStream方法是多线程处理(这个较容易被忽视)。注意使用时的并发访问共享资源带来的安全问题