首页>>后端>>java->线程池技术实战讲解,入门必读!

线程池技术实战讲解,入门必读!

时间:2023-12-02 本站 点击:0

线程池实战

简介:

上文讲述了怎么写一个简单的数据库连接池,试想一下,当服务端接收到来自客户端的成白上千个连接请求时,如果每次我们都为其创建一个线程去执行任务,执行完毕后销毁线程,这肯定不是一个好的解决办法。我们知道线程的创建和销毁是要消耗系统资源的,那么我们就可以从这个方面入手来控制资源的浪费。

线程池描述:

线程池技术是一种池化思想,也就是提前预置若干数量的线程,并且线程不能由用户直接控制,在这个前提下重复使用固定数量或者可以特定条件下进行伸缩的线程数目来完成任务的执行。

线程池的优点:

消除频繁创建和消亡线程带来的系统开销

面对过量任务提交时能够平缓的劣化

代码示例:

1、接口定义

描述,接口定义了提交任务到线程池执行的方法execute(Job job),也提供增大/减少工作者线程和关闭线程的方法。工作者指的是,执行客户端提交任务的真实工作线程,工作者线程会从一个工作队列中获取等待执行的Job进行处理。

packagecom.lizba.p3.threadpool;/***<p>*线程池接口*</p>**@Author:Liziba*@Date:2021/6/1722:28*/publicinterfaceThreadPool<JobextendsRunnable>{/***执行一个Job,这个Job需要实现Runnable*@paramjob*/voidexecute(Jobjob);/***关闭线程池*/voidshutdown();/***增加工作者线程*@paramnum*/voidaddWorkers(intnum);/***减少工作者线程*@paramnum*/voidremoveWorkers(intnum);/***得到正在等待执行的任务数量*@return*/intgetJobSize();}

2、默认实现

packagecom.lizba.p3.threadpool;importjava.util.ArrayList;importjava.util.Collections;importjava.util.LinkedList;importjava.util.List;importjava.util.concurrent.atomic.AtomicLong;/***<p>*线程池默认实现*</p>**@Author:Liziba*@Date:2021/6/1722:34*/publicclassDefaultThreadPool<JobextendsRunnable>implementsThreadPool<Job>{/**线程池最大工作者线程数量*/privatestaticfinalintMAX_WORKER_SIZE=20;/**线程池默认工作者线程数量*/privatestaticfinalintDEFAULT_WORKER_SIZE=5;/**线程池最小工作者线程数量*/privatestaticfinalintMIN_WORKER_SIZE=5;/**工作队列,也称任务队列,用来存放客户端提交的任务*/privatefinalLinkedList<Job>jobs=newLinkedList<>();/**工作者列表,需要具有同步性质,支持并发操作*/privatefinalList<Worker>workers=Collections.synchronizedList(newArrayList<Worker>());/**工作线程的数量*/privateintworkerNum=DEFAULT_WORKER_SIZE;/**线程编号生成器*/privateAtomicLongthreadNum=newAtomicLong();publicDefaultThreadPool(){initWorker(DEFAULT_WORKER_SIZE);}/***初始化线程工作者,并启动**@paramsize初始化工作着大小*/privatevoidinitWorker(intsize){for(inti=0;i<size;i++){Workerworker=newWorker();workers.add(worker);Threadthread=newThread(worker,"ThreadPool-Worker-"+threadNum.incrementAndGet());thread.start();}}@Overridepublicvoidexecute(Jobjob){if(job!=null){//添加一个任务,然后通知等待在jobs上的workersynchronized(jobs){jobs.add(job);jobs.notifyAll();}}}@Overridepublicvoidshutdown(){workers.forEach(worker->worker.shutdown());}@OverridepublicvoidaddWorkers(intnum){//此处要锁住jobs,因为worker会从jobs获取任务,需要jobs通知等待中的workersynchronized(jobs){//不允许工作者线程数操作最大值if(num+this.workerNum>MAX_WORKER_SIZE){num=MAX_WORKER_SIZE-this.workerNum;}initWorker(num);this.workerNum+=num;}}@OverridepublicvoidremoveWorkers(intnum){synchronized(jobs){if(num>this.workerNum){thrownewIllegalArgumentException("超出工作者数目!");}intcount=0;while(count<num){Workerworker=workers.get(count);//如果移除成功则关闭工作者,工作者将不会继续获取任务执行if(workers.remove(worker)){worker.shutdown();count++;}this.workerNum-=count;}}}@OverridepublicintgetJobSize(){returnjobs.size();}/***<p>*工作者-负责消费客户端提交的任务*</p>**@Author:Liziba*@Date:2021/6/1722:41*/classWorkerimplementsRunnable{/**是否工作*/privatevolatilebooleanrunning=Boolean.TRUE;@Overridepublicvoidrun(){while(running){Jobjob=null;synchronized(jobs){while(jobs.isEmpty()){try{jobs.wait();}catch(InterruptedExceptione){//如果感应到外部的中断通知,则自己主动中断返回Thread.currentThread().interrupt();return;}}//取出任务队列的第一个任务job=jobs.removeFirst();}//执行任务if(job!=null){try{job.run();}catch(Exceptione){e.printStackTrace();}}}}/***关闭worker,全部关闭意味着线程池关闭*/publicvoidshutdown(){running=false;}}}

测试

packagecom.lizba.p3.threadpool;importcom.lizba.p2.SleepUtil;/***<p>*线程池测试*</p>**@Author:Liziba*@Date:2021/6/1723:19*/publicclassPoolTest{publicstaticvoidmain(String[]args){DefaultThreadPoolpool=newDefaultThreadPool();//提交10个任务intsize=10;for(inti=0;i<size;i++){Threadjob=newThread(newRunnable(){@Overridepublicvoidrun(){SleepUtil.sleepSecond(1);System.out.println(Thread.currentThread().getName()+"执行Job任务");}});pool.execute(job);}}}

查看输出:

3、总结

从上述代码可以看出,线程池的本质就是使用一个线程安全工作队列来连接工作线程和客户端线程。当客户端调用execute(job)方法提交一个任务时,线程池会向任务列表jobs中添加任务Job,而工作者worker会不断的从jobs中取出一个Job执行,当Job为空时,工作者会进入等待状态。而这个等待/通知就是使用的wait()/notifyAll()来实现的。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/7732.html