java线程池粗解----结合现实买票实例(1)

      在java中,线程的执行顺序大致为,创建线程,然后启动线程,线程就进入到就绪的状态,接着就等CPU分配资源运行线程,运行完后销毁。

      下面我就用现实中的汽车站买票来解释线程以及线程池的作用,如一般的线程,在买票的过程就是,准备钱(就绪状态),准备好钱后去售票窗口排队(等待),等排到你后服务员(CPU)打开窗口(分配资源)给你进行买票操作(运行线程),最后你买到了票就离开(销毁),同时这个买票的窗口也关闭了(回收资源)。而现实就会出现一些情况,比如买票的人很多,每有一个人要买票时,就要让服务员重新打开窗口,买完票后又要关掉窗口。下一个人买票再打开窗口......再关闭窗口。想想就知道这有多麻烦,转换到编程中也是如此,所以频繁地创建线程和销毁线程会导致CPU利用率低,浪费资源,因为线程是重要的资源。所以便有了线程池。

       针对这样的情况,于是线程池就干了一件很不道德的事情,那就是占用这个窗口(CPU资源),因为当一个人买票后服务员(CPU)一定会关窗口,等下次有人再打开,于是线程池里的线程做的事情就是一直占用这个窗口,一直不让服务员小姐姐关窗口,和她唠嗑,等下一个人(下个线程)来的时候,只需要把钱(线程对象)直接给这个线程,连排队都不用了(因为CPU跑哪一个线程是直接从就绪状态的线程中拿一个来跑的,所以当你跑线程的时候,其实在跑之前它是在排队的,平时我们看不出时间上有多少差别,因为排队线程少,但是大的系统跑起来的时候这个排队等待的时间就会很明显,处理不好很影响系统的效率),直接插队拿给小姐姐买票,买完票后再和小姐姐唠嗑,不让她关窗口,用这样的方式,线程池中的线程其实一直在跑,一直占用着CPU的资源没有释放,所以当有新的人要买票时(新的线程),可以直接买票(把线程对象放到线程池中的线程运行),不用排队。

       线程池中的参数,corePoolSize:核心池的大小。其实代表的就是线程池中开几个线程,比如10个,那就相当于买票时派几个人去占用窗口。但是这个不是马上就派10个人出去占用窗口的(不是马上创建10个线程)。步骤:第一个人去买票时,先看看有几个人占用窗口(查看线程池的线程数有没有超过corePoolSize的数量10个),如果没有人或者说不超过10个人时,在买完票后就开始耍流氓,占用窗口,不让小姐姐关窗口。当第二个人买票时也重复这个操作,发现占用窗口只有一个人,于是买完票也去书流氓,再占用另一个窗口,以此类推,当十个人买完票后。这个车站(CPU)就有10个窗口(线程资源)给你占了,后面再有人要买票,就直接找我们的总黄牛(workQueue队列),让他派人到我们占用的这些窗口买票,查看这10个窗口哪个窗口是空闲的就去买票,全部都不用排队了(这个线程池分派任务用的是workQueue阻塞队列来实现,当线程池 的数量达到corePoolSize的时候,以后开的每一个线程都会扔进这个队列中,让这个队列去分派任务给线程池的线程)。

        线程池中在队列运用上一般用的是有界队列,就如同黄牛,他手里只能接20个买票的单,如果手里的单超过了20个,他就会和第21个人说买不了票,要等一下(线程池在这种情况下是会报异常的),等这个黄牛把手里的单解决完或者解决了1个后,他手里的单就变成了19个,就能再接一个人的单了。如果用无界队列LinkedBlockingQueue,当遇到春运,一下子几千几万人来找这个黄牛,黄牛又没办法拒绝(因为是无界的),所以他手里的单突然上升到了几千几万,人很有可能会崩溃(系统崩溃,内存溢出之类的

        当买票人多的时候,黄牛手上的单可能会很高,如20个单,但是占用的窗口就只有10个,没办法同时办20个,如果等的话黄牛手上的单可能越积越多,导致系统效率变低,这时候就要看另一个线程池参数,maximumPoolSize:线程池最大线程数。如最大线程数是20,那当出现上述情况时,线程池就会再派一个人去买票并且临时占用窗口,和前面10个线程占用窗口步骤一样,一直占用直到占用窗口总数达到20个,这样黄牛就能同时派出20个买票任务了,线程池用这样的方式来解决突发的线程数量激增的情况,同时还有线程池的参数,keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。比如5分钟,当买票的人数慢慢变小时,原来占用的10个窗口就已经足以应对,这时候如果那10个临时占用的窗口空闲时间超过了5分钟,那个占用窗口的人就会离开(释放临时线程的CPU资源,缓解CPU压力)。这种设计让线程池更加有弹性,可以动态的处理线程数量变化的情况。(有的人觉得如果把线程池大小一开始就设置很大不好吗?但是你要考虑到CPU的使用情况,线程池的线程会一直占用CPU资源,这些资源是很宝贵的,不能随便占用,就好像,一天买票的就只有1个人,你却占用着10个窗口,太浪费资源了,线程数和最大线程数都是要经过计算来取值的,有一套专门的算法来计算

       以上就是我对线程池的理解,另外之前我在想java是怎么让线程池的线程一直跑的,后面看了源码才知道,它是利用了while循环,(类似于socket服务端的while(true)来一直获取客户端请求),线程池里的线程在执行完run()方法后会有个while循环,里面的条件是执行workQueue队列的take()方法,这个方法会去获取队列的值(也就是新的线程对象),如果获取不到就会一直阻塞,直到黄牛(workQueue)有新的单子进入(新的线程加入),这样获取的到的新的线程对象就会被while循环获取,然后执行线程的run()方法,然后继续跑while循环,再阻塞,来达到线程池里的线程一直占用CPU资源的效果,因为它根本就没停过。同样的那些线程池临时增加的线程,他们用的是workQueue的poll()方法,这个方法也是获取队列的值,但是它有一个超时时间(keepAliveTime)当超过这个时间还没有获取到新的线程时,就会返回null,就可以认为线程池的压力已经缓解,可以释放掉多余的资源了,然后这些临时线程就会给释放。

        上面的说法对应的代码实例以后再补充,欢迎转载,谢谢!

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值