前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java并发编程与高并发之多线程

Java并发编程与高并发之多线程

作者头像
别先生
发布2020-02-18 11:40:23
6060
发布2020-02-18 11:40:23
举报
文章被收录于专栏:别先生别先生

1、线程池,初始化好线程池的实例以后,将要执行的任务丢到线程池里面,等待任务的调度执行。

2、使用new Thread的弊端。

  弊端一、每次new Thread新建对象,性能差,   弊端二、线程缺乏统一管理,可以无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM。   弊端三,缺少更多功能,如更多执行,定期执行,线程中断。

3、使用线程池的好处。

  好处一、重用存在的线程,减少对象创建,消亡的开销,性能佳。     好处二、可以有效的控制最大并发的线程数,提高系统资源的利用率,同时可以避免过多的资源竞争,避免阻塞。   好处三、提高定时执行、定期执行,单线程,并发数控制等功能。

4、线程池ThreadPoolExecutor

  1)、参数一、corePoolSize,核心线程数量。   2)、参数二、maximumPoolSize,线程最大线程数。   3)、参数三、workQueue,阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响。

    保存等待任务的阻塞队列,当提交新的任务到线程池以后,线程池会根据当前线程池中正在运行着的线程的数量来决定该任务的处理方式,处理方式主要有三种,直接切换、使用无界队列、使用有界队列。直接切换这种常用的队列就是阻塞队列里面的SynchronizedQueue,使用无界队列是一般使用基于链表的阻塞队列LinkedBlockingQueue,如果使用这种无界队列的方式呢,线程池中能够创建的最大线程数是corePoolSize,而maximumPoolSize就不会起作用了,当线程池中所有的核心线程都是运行状态的时候,一个新的任务提交以后就会放到等待队列里面去,当workQueue为有界队列的时候,一般使用的是ArrayBlockingQueue,使用这种方式可以将线程池最大线程数量限制为maximumPoolSize,这样可以降低资源的消耗,但是,这种方式使得线程池对线程调度变得更困难,因为我们的线程池和队列容量都是有限的,所以要想使线程池处理任务和吞吐率达到一个相对合理的范围,又想使线程调度相对简单,并且还能尽可能降低线程池对资源的消耗,我们需要合理的设置这两个数量corePoolSize、maximumPoolSize。具体说一下,如果想降低系统资源的消耗,包括CPU的使用率,操作系统资源的消耗,环境切换开销等等,可以设置一个较大的队列容量,和较小的线程池容量,这样会降低线程处理任务的吞吐量。如果我们提交的任务经常发生阻塞,可以考虑调用设置maximumPoolSize,线程最大线程数,重新设置线程池的容量。如果我们的队列容量设置较小,通常需要将这个线程池容量设置较大一些,这样CPU的使用率会较高一些。但是如果线程池容量设置过大,在提交任务过多的情况下,并发量会增加,那么线程之间的调度就是一个需要考虑的问题,这样反而会降低处理任务的吞吐量。

    corePoolSize、maximumPoolSize、workQueue三个参数的关系。 如果运行的线程数少于corePoolSize核心线程数量的时候,直接创建新的线程执行任务,即使线程池中的其他线程是空闲的,如果线程池中的线程大于等于corePoolSize核心线程数量的时候,且小于maximumPoolSize,线程最大线程数的时候,则只有当workQueue满的时候才创建新的线程处理任务。如果我们设置的corePoolSize和maximumPoolSize相同的时候,那么创建的线程池的大小是固定的,这个时候如果有新的任务提交,如果workQueue还没有满的时候,就将请求放入到workQueue中,等待有空闲的线程从workQueue取出任务,进行处理。如果我们运行的线程数量大于maximumPoolSize,此时如果workQueue也已经满了,通过拒绝策略的参数指定策略来处理任务。所以任务提交的时候判断顺序主要有三个,第一个看corePoolSize,如果corePoolSize小于maximumPoolSize就直接创建线程调用了,接下来看的是workQueue,最后看到的是maximumPoolSize。

  4)、参数四、keepAliveTime,线程没有任务执行最多保持多久时间终止。线程池维护线程所允许的空闲时间,当线程池中的线程数量大于corePoolSize的时候,如果此时没有新的任务提交,核心线程不会立即销毁,而是进行等待,直到等待的时间超过keepAliveTime。   5)、参数五、unit,keepAliveTime的时间单位。   6)、参数六、threadFactory,线程工厂,用来创建线程。默认有一个默认的工厂创建线程,使用默认的工厂创建线程的时候,会使新创建的工厂具有相同的优先级,并且是非守护的线程,同时也设置了线程的名称。   7)、参数七、rejectHanbler,拒绝策略,当拒绝处理任务时候的策略。如果workQueue对应的阻塞队列满了,并且没有空闲的线程池,此时,如果继续提交任务,就需要采取一种策略来进行处理这个任务,线程池一共提供了四种策略,第一种就是直接抛出异常,也是默认的策略,第二种用调用者所在的线程执行任务,第三种是丢弃线程中最靠前的任务,并执行当前任务,相当于好久之前的任务就不要了,直接丢弃掉,直接执行当前任务,第四种是直接丢弃该任务。

5、线程池中的几种状态。如下所示:

当我们初始化一个线程池以后,通常有以下几种状态,Running、Shutdown、Stop、Tidying、Terminated。这些状态我们不需要特别处理,是线程池内部根据方法来定义线程池的状态的,有印象即可。   1)、Running是判断接收新提交的任务,并且也能处理阻塞队列里面的任务。   2)、Shutdown是属于关闭状态,当一个线程池实例处于Shutdown状态的时候,不能再接收新提交的任务,但是可以处理阻塞队列中保存的任务,在线程池处于Running时,调用shutdown()方法的时候,会使线程池进入该状态。   3)、Stop状态,当一个线程池实例处于Stop状态的时候,不能再接收新提交的任务,也不能处理阻塞队列中保存的任务。中断正在处理任务的线程,在线程池处于Running或者Shutdown时,调用shutdownNow()方法的时候,会使线程池进入该状态。   4)、Tidying状态,如果所有的任务都已经终止,有效线程数量为0,就进入该状态了。   5)、Terminated状态,之后Tidying状态调用terminated()方法进入该状态。

6、线程池ThreadPoolExecutor的方法介绍。

  1)、execute()方法,提交任务,交给线程池执行。当有了线程池实例以后呢,就需要将线程放入到线程池里面的,调度执行,调度方法就是execute方法。   2)、submit()方法,提交任务,能够返回执行结果,相当于execute + Future方法的组合。submit方法和execute类似,都是提交任务,但是submit方法可以返回结果,拿到返回结果继续处理。   3)、shutdown()方法,关闭线程池,等待任务都执行完。线程池使用完毕以后,记得关闭线程池。   4)、shutdownNow()方法,关闭线程池,不等待任务执行完。不等待任务执行完,直接关闭掉了,暂停正在执行的线程。使用场景,不得不暂停所有任务,关闭线程池。   5)、getTaskCount()方法,线程池已经执行和未执行的任务总数。适用于监控的方法。   6)、getCompletedTaskCount()方法,已经完成的任务数量。适用于监控的方法。   7)、getPoolSize()方法,线程池当前的线程数量。适用于监控的方法。   8)、getActiveCount()方法,当前线程池中正在执行任务的线程数量。适用于监控的方法。

7、线程池类图,如下所示:

Executor框架是根据一组执行策略的调用,调度,执行和控制的异步任务的框架,目的是提高一种将任务提交与任务如何运行分离开的机制。JUC里面有三个Executor接口,分别如下所示:   1)、Executor,运行先任务的简单接口。   2)、ExecutorService,扩展了Executor接口,添加了用来管理执行器生命周期和任务生命周期的方法。   3)、ScheduledExecutorService,扩展了ExecutorService接口,支持Future和定期执行任务。 ThreadPoolExecutor是功能最强的,可以根据功能需求传入我们需要的参数,以及指定任何策略。 8、线程池Executor框架接口提供的方法。

  1)、Executors.newCachedThreadPool,命名了一个新的线程池,创建了一个可以缓存的线程池,如果线程池的长度超过了处理的需要,可以灵活回收空闲线程,如果没有可以回收的,就新建线程。   2)、Executors.newFixedThreadPool,创建一个定长的线程池,可以控制线程的最大并发数目,超出的线程会在队列里面等待。   3)、Executors.newScheduledThreadPool,创建一个定长的线程池,支持定时和周期性的任务执行。   4)、Executors.newSingleThreadExecutor,创建的是一个单线程化的线程池,只会用唯一的一个工作线程执行任务,保证所有任务按照指定顺序执行,可以指定先入先出,优先级等等进行执行。

注意:线程池的合理配置,CPU密集型任务,就需要尽量压榨CPU,参考值可以设置为NCPU+ 1。如果是IO密集型任务,参考值可以设置为2*NCPU。

 1 package com.bie.concurrency.example.threadpool;
 2 
 3 import java.util.concurrent.ExecutorService;
 4 import java.util.concurrent.Executors;
 5 
 6 import lombok.extern.slf4j.Slf4j;
 7 
 8 /**
 9  * 
10  *
11  * @Title: ThreadPoolExample1.java
12  * @Package com.bie.concurrency.example.threadpool
13  * @Description: TODO
14  * @author biehl
15  * @date 2020年1月20日
16  * @version V1.0
17  * 
18  *          线程池的使用
19  */
20 @Slf4j
21 public class ThreadPoolExample1 {
22 
23     public static void main(String[] args) {
24 
25         // Executors.newCachedThreadPool,命名了一个新的线程池,创建了一个可以缓存的线程池,
26         // 如果线程池的长度超过了处理的需要,可以灵活回收空闲线程,如果没有可以回收的,就新建线程。
27         // ExecutorService executorService = Executors.newCachedThreadPool();
28 
29         // Executors.newFixedThreadPool,创建一个定长的线程池,可以控制线程的最大并发数目,超出的线程会在队列里面等待。
30         // ExecutorService executorService = Executors.newFixedThreadPool(3);
31 
32         // Executors.newSingleThreadExecutor,创建的是一个单线程化的线程池,
33         // 只会用唯一的一个工作线程执行任务,保证所有任务按照指定顺序执行,可以指定先入先出,优先级等等进行执行。
34         ExecutorService executorService = Executors.newSingleThreadExecutor();
35 
36         for (int i = 0; i < 10; i++) {
37             final int index = i;
38 
39             executorService.execute(new Runnable() {
40 
41                 @Override
42                 public void run() {
43                     log.info("搞事情序号: {} ", index);
44                 }
45             });
46         }
47         executorService.shutdown();
48     }
49 
50 }
 1 package com.bie.concurrency.example.threadpool;
 2 
 3 import java.util.Date;
 4 import java.util.Timer;
 5 import java.util.TimerTask;
 6 import java.util.concurrent.Executors;
 7 import java.util.concurrent.ScheduledExecutorService;
 8 
 9 import lombok.extern.slf4j.Slf4j;
10 
11 /**
12  * 
13  *
14  * @Title: ThreadPoolExample1.java
15  * @Package com.bie.concurrency.example.threadpool
16  * @Description: TODO
17  * @author biehl
18  * @date 2020年1月20日
19  * @version V1.0
20  * 
21  *          线程池的使用
22  */
23 @Slf4j
24 public class ThreadPoolExample2 {
25 
26     public static void main(String[] args) {
27 
28         ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
29 
30         // long initialDelay = 1;// 线程池调度放行以后,延迟一秒
31         // long period = 3;// 间隔三秒执行
32         // TimeUnit unit = TimeUnit.SECONDS;// 秒的单位
33         // executorService.scheduleAtFixedRate(new Runnable() {
34         //
35         // @Override
36         // public void run() {
37         // log.warn("schedule run......");
38         //
39         // }
40         // }, initialDelay, period, unit);
41 
42         // executorService.shutdown();// 线程关闭
43 
44         // 定时器
45         Timer timer = new Timer();
46         Date time = new Date(); // 初始时间
47         long period2 = 5 * 1000;// 间隔5秒
48         timer.schedule(new TimerTask() {
49 
50             @Override
51             public void run() {
52                 log.warn("timer run");
53             }
54         }, time, period2);
55 
56     }
57 
58 }

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-01-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档