银行java技术_「技术」其实银行就是一个Java线程池

da96f4829250e990f98905e6dce063ec.png

银行实际上就是一个Java线程池。

一次周末去银行办业务,人多排号,排的久了突然发现银行实际上和Java的线程池如此类似,可以说一模一样。于是我就展开了联想。

银行是个线程池周末,银行七个窗口只来了两个值班的员工,那么这两个员工我们就叫做核心员工数那天由于不知道什么原因,来银行办业务的人特别多,两个员工很快就忙不过来了,那么新来的人怎么办呢?只能排个号在休息区的椅子上坐着等,这一排椅子我们就叫做等待队列很快,休息区也坐满了人,这个时候银行的处理业务的效率明显跟不上了,咋办呢?可以叫人会来加班(创建新的线程),由于银行只有7个窗口,所以最终只能有7个员工。最多七个柜台就叫做最大员工数有了新的员工,人多了,处理业务的速度也上来了,会出现两种情况虽然银行已经满负载了,但是人还是源源不断的来银行办业务,这个时候所有的工作人员都忙着,等待区也坐满了人,那么新的人怎么办呢?工作人员可以选择让新的员工明儿,或者一会儿再来,这个让新的人什么时候来的做法,就是一种拒绝策略的选择。除了上面的情况还会有另一种情况,5个人来加班之后,效率提升,很快就把所有的业务做完了,然后银行就没有人办业务了,那么这多出来的五个人要一直在银行待着吗?肯定不是,他们会等一会儿然后回家,这个等一会儿我们可以叫做空闲等待时间以上银行的流程和线程池的流程完全是一样的,我们只需要将

银行工作人员换成线程办业务的人换成任务对象(Runnable)休息区换成等待队列......上述的步骤就和线程池的原理,基本参数一模一样了。

说完了线程池的基本原理,接下来就是线程池常见的面试问题。

什么是线程池?有什么好处?

谈到线程池就会想到池化技术,其中最核心的思想就是把宝贵的资源放到一个池子中;每次使用都从里面获取,用完之后又放回池子供其他人使用,有点吃大锅饭的意思。

Java线程池有以下优点:

线程是稀缺资源,不能频繁的创建。解耦作用;线程的创建于执行完全分开,方便维护。应当将其放入一个池子中,可以给其他任务进行复用。创建线程池的方式

通过Executors类通过ThreadPoolExecutor类

在Java中,我们可以通过Executors类创建线程池,常见的API有:

Executors.newCachedThreadPool():无限线程池。Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。Executors.newSingleThreadExecutor():创建单个线程的线程池。Executors.newScheduledThreadPool()Executors.newWorkStealingPool(int) java8新增,使用目前机器上可用的处理器作为它的并行级别以上的这些创建线程池的方法,实际上JDK已经给我们写好的,可以拿来即用的。但是只要我们查看上述方法的源码就会发现:

public static ExecutorService newCachedThreadPool() {

return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,

new SynchronousQueue());

}

以上方法实际上都是利用 ThreadPoolExecutor 类实现的。

所以第二种创建线程方式是自己通过 new ThreadPoolExecutor来进行创建。

Executors 有那么多创建线程池的方法,开发中用哪个比较好?

答案:一个都不用。

从《阿里巴巴Java开发手册》中可以看到

63205fb301cf8d238ccc05f35d47d3e2.png

如何通过 ThreadPoolExecutor 自定义线程池?即线程池有哪些重要的参数?

在上一个问题中,我们提到了创建线程池要通过 new ThreadPoolExecutor 的方式,那么,如何创建呢?在创建的时候,又需要哪些参数呢?

我们直接看一下 ThreadPoolExecutor 的构造方法源码,如下:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {

if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)

throw new IllegalArgumentException();

if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException();

this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();

this.corePoolSize = corePoolSize;

this.maximumPoolSize = maximumPoolSize;

this.workQueue = workQueue;

this.keepAliveTime = unit.toNanos(keepAliveTime);

this.threadFactory = threadFactory;

this.handler = handler;

}

密密麻麻都是参数,那么这些参数都什么呢?

4cf8a23f5cc2abf51bd21316cd5031fa.png

大致的流程就是

创建线程池之后,有任务提交给线程池,会先由 核心线程执行如果任务持续增加,corePoolSize用完并且任务队列满了,这个时候线程池会增加线程的数量,增大到最大线程数这个时候如果任务继续增加,那么由于线程数量已经达到最大线程数,等待队列也已经满了,这个时候线程池实际上是没有能力执行新的任务的,就会采用拒绝策略如果任务量下降,就会有很多线程是不需要的,无所事事,而只要这些线程空闲的时间超过空闲线程时间,就会被销毁,直到剩余线程数为corePoolSize。通过以上参数可以就可以灵活的设置一个线程池了,示例代码如下:

/*** 获取cpu核心数*/

private static int corePoolSize = Runtime.getRuntime().availableProcessors();

/** * corePoolSize用于指定核心线程数量 *

maximumPoolSize指定最大线程数

* keepAliveTime和TimeUnit指定线程空闲后的最大存活时间 */

public static ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10l, TimeUnit.SECONDS, new LinkedBlockingQueue(1000));

线程池底层工作原理?

关于线程池的工作原理和执行流程,通过两张图来进行展示

26a1a41c1a022570169f17db14f7ebdf.png

38827eca457c4121438e55e188a9cb2b.png

在创建了线程池后,等待提交过来的任务请求。当调用execute()方法添加一个请求任务时,线程池会做如下判断:如果正在运行的线程数量小于corePoolSize,那么马上创建马上创建线程运行这个任务。如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列。如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务。如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。当一个线程完成任务时,它会从队列中取下一个任务来执行。当一个线程无事可做超过一定的时间(keepAlilveTime)时,线程池会判断:如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后它最终会收缩到corePoolSize的大小。谈谈线程池的饱和策略,也叫做拒绝策略。

所谓饱和策略就是:当等待队列已经排满,再也发不下新的任务的时候,这时,线程池的最大线程数也到了最大值,意味着线程池没有能力继续执行新任务了,这个时候再有新任务提交到线程池,如何进行处理,就是饱和(拒绝)策略

655daddcc7e7e7fd95aaac82b80f2ac0.png

如何合理配置一个线程池

通常我们是需要根据这批任务执行的性质来确定的。

IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2IO密集型,即该任务需要大量的IO,即大量的阻塞。在单线程上运行IO密集型的任务会导致浪费大量的CPU运算能力浪费在等待。所以IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要就是利用了被浪费掉的阻塞时间。CPU 密集型任务(大量复杂的运算)应当分配较少的线程,比如 CPU 个数相当的大小。CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU一直全速运行。当然这些都是经验值,最好的方式还是根据实际情况测试得出最佳配置。

如何关闭线程池

关闭线程池的方法有两个:

shutdown()/shutdownNow()

shutdown()执行后停止接受新任务,会把队列的任务执行完毕。shutdownNow()也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。关闭线程池的代码:

long start = System.currentTimeMillis();

for (int i = 0; i <= 5; i++) {

pool.execute(new Job());

}

pool.shutdown();

while (!pool.awaitTermination(1, TimeUnit.SECONDS)) {

LOGGER.info("线程还在执行。。。");

}

long end = System.currentTimeMillis();

LOGGER.info("一共处理了【{}】", (end - start));

pool.awaitTermination(1, TimeUnit.SECONDS)

会每隔一秒钟检查一次是否执行完毕(状态为

TERMINATED

),当从 while 循环退出时就表明线程池已经完全终止了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值