您好,欢迎访问三七文档
当前位置:首页 > 金融/证券 > 综合/其它 > 微交易系统开发语言之Java线程池
微交易系统开发学习之Java线程池线程池的技术背景在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些”池化资源”技术产生的原因。例如Android中常见到的很多通用组件一般都离不开”池”的概念,如各种图片加载库,网络请求库,即使Android的消息传递机制中的Meaasge当使用Meaasge.obtain()就是使用的Meaasge池中的对象,因此这个概念很重要。本文将介绍的线程池技术同样符合这一思想。线程池的优点:重用线程池中的线程,减少因对象创建,销毁所带来的性能开销;能有效的控制线程的最大并发数,提高系统资源利用率,同时避免过多的资源竞争,避免堵塞;能够多线程进行简单的管理,使线程的使用简单、高效。线程池框架Executorjava中的线程池是通过Executor框架实现的,Executor框架包括类:Executor,Executors,ExecutorService,ThreadPoolExecutor,Callable和Future、FutureTask的使用等。Executor:所有线程池的接口,只有一个方法。123publicinterfaceExecutor{voidexecute(Runnablecommand);}ExecutorService:增加Executor的行为,是Executor实现类的最直接接口。Executors:提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。ThreadPoolExecutor:线程池的具体实现类,一般用的各种线程池都是基于这个类实现的。构造方法如下:1234567publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueueRunnableworkQueue){this(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,8910Executors.defaultThreadFactory(),defaultHandler);}corePoolSize:线程池的核心线程数,线程池中运行的线程数也永远不会超过corePoolSize个,默认情况下可以一直存活。可以通过设置allowCoreThreadTimeOut为True,此时核心线程数就是0,此时keepAliveTime控制所有线程的超时时间。maximumPoolSize:线程池允许的最大线程数;keepAliveTime:指的是空闲线程结束的超时时间;unit:是一个枚举,表示keepAliveTime的单位;workQueue:表示存放任务的BlockingQueueRunnable队列。BlockingQueue:阻塞队列(BlockingQueue)是java.util.concurrent下的主要用来控制线程同步的工具。如果BlockQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒。同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。具体的实现类有LinkedBlockingQueue,ArrayBlockingQueued等。一般其内部的都是通过Lock和Condition(显示锁(Lock)及Condition的学习与使用)来实现阻塞和唤醒。线程池的工作过程如下1.线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。2.当调用execute()方法添加一个任务时,线程池会做如下判断:3.如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;如果这时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会抛出异常RejectExecutionException。4.当一个线程完成任务时,它会从队列中取下一个任务来执行。5.当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,6.它最终会收缩到corePoolSize的大小。线程池的创建和使用生成线程池采用了工具类Executors的静态方法,以下是几种常见的线程池。SingleThreadExecutor:单个后台线程(其缓冲队列是无界的)123456publicstaticExecutorServicenewSingleThreadExecutor(){returnnewFinalizableDelegatedExecutorService(newThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,newLinkedBlockingQueueRunnable()));}创建一个单线程的线程池。这个线程池只有一个核心线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。FixedThreadPool:只有核心线程的线程池,大小固定(其缓冲队列是无界的)。12345publicstaticExecutorServicenewFixedThreadPool(intnThreads){returnnewThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,newLinkedBlockingQueueRunnable());}创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。CachedThreadPool:无界线程池,可以进行自动线程回收。12publicstaticExecutorServicenewCachedThreadPool(){returnnewThreadPoolExecutor(0,Integer.MAX_VALUE,34560L,TimeUnit.SECONDS,newSynchronousQueueRunnable());}如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。SynchronousQueue是一个是缓冲区为1的阻塞队列。ScheduledThreadPool:核心线程池固定,大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。123456publicstaticExecutorServicenewScheduledThreadPool(intcorePoolSize){returnnewScheduledThreadPool(corePoolSize,Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS,MILLISECONDS,newDelayedWorkQueue());}创建一个周期性执行任务的线程池。如果闲置,非核心线程池会在DEFAULT_KEEPALIVEMILLIS时间内回收。线程池最常用的提交任务的方法有两种:execute:1ExecutorService.execute(Runnablerunable);submit:12345FutureTasktask=ExecutorService.submit(Runnablerunnable);FutureTaskTtask=ExecutorService.submit(Runnablerunnable,TResult);FutureTaskTtask=ExecutorService.submit(CallableTcallable);submit(Callablecallable)的实现,submit(Runnablerunnable)同理。123456publicTFutureTsubmit(CallableTtask){if(task==null)thrownewNullPointerException();FutureTaskTftask=newTaskFor(task);execute(ftask);returnftask;}可以看出submit开启的是有返回结果的任务,会返回一个FutureTask对象,这样就能通过get()方法得到结果。submit最终调用的也是execute(Runnablerunable),submit只是将Callable对象或Runnable封装成一个FutureTask对象,因为FutureTask是个Runnable,所以可以在execute中执行。关于Callable对象和Runnable怎么封装成FutureTask对象,见Callable和Future、FutureTask的使用。线程池实现的原理如果只讲线程池的使用,那这篇博客没有什么大的价值,充其量也就是熟悉Executor相关API的过程。线程池的实现过程没有用到Synchronized关键字,用的都是volatile,Lock和同步(阻塞)队列,Atomic相关类,FutureTask等等,因为后者的性能更优。理解的过程可以很好的学习源码中并发控制的思想。在开篇提到过线程池的优点是可总结为以下三点:1.线程复用2.控制最大并发数3.管理线程1.线程复用过程理解线程复用原理首先应了解线程生命周期。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。Thread通过new来新建一个线程,这个过程是是初始化一些线程信息,如线程名,id,线程所属group等,可以认为只是个普通的对象。调用Thread的start()后Java虚拟机会为其创建方法调用栈和程序计数器,同时将hasBeenStarted为true,之后调用start方法就会有异常。处于这个状态中的线程并没有开始运行,只是表示该线程可以运行了。至于该线程何时开始运行,取决于JVM里线程调度器的调度。当线程获取cpu后,run()方法会被调用。不要自己去调用Thread的run()方法。之后根据CPU的调度在就绪——运行——阻塞间切换,直到run()方法结束或其他方式停止线程,进入dead状态。所以实现线程复用的原理应该就是要保持线程处于存活状态(就绪,运行或阻塞)。接下来来看下ThreadPoolExecutor是怎么实现线程复用的。在Threa
本文标题:微交易系统开发语言之Java线程池
链接地址:https://www.777doc.com/doc-2435215 .html