在java并发编程中,线程池是一个比较重要的点,什么时候需要使用线程池,什么时候不需要使用线程池,看不同的需求,众所周知,新增一个线程是比较耗资源的,因此如果每次新增一个任务就添加一个线程,在分时系统中,这不仅会造成每个线程所获得的执行时间大大降低,同时也会使cpu和内存大大消耗,线程池是一种比较合适的处理办法,一方面缓解资源紧张,一方面又能获得不错的性能,但是,对于批处理作业和耗费资源不是很多的任务,选择线程池不是一个很好地设计办法。
首先看看两个新的接口,Callable和Future源码如下
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
package java.util.concurrent; public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
简而言之,callable接口类似Runnable 接口,其call()方法和Runnable的run()方法很相似,但是Callable有返回值,而Runnable没有返回值。Future保存异步计算的结果。可以启动一个计算,将Future对象交给某个线程,然后忘掉它,也就是当他是一个返回值。
通常在一般线程中会使用FutureTask类,FutureTask接口继承自RunnableFuture,而Runnable接口继承Runnable和Future。
首先看下FutureTask的简单用法:
package com.luchi.thread.threadpool; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class TestFutureRCallable implements Callable<Integer>{ private int counter=0; @Override public Integer call() throws Exception { // TODO Auto-generated method stub System.out.println("i am on the running"); return 1; } public static void main(String[]args) throws InterruptedException, ExecutionException{ TestFutureRCallable testThread=new TestFutureRCallable(); FutureTask<Integer>futureTask=new FutureTask<Integer>(testThread); Thread thread=new Thread(futureTask); thread.start(); System.out.println("future returns:"+futureTask.get()); } }
上面程序把Callable的继承类当做FutureTask构造函数参数,然后运行Thread,最后FutureTask能够得到返回值。
FutureTask有几个构造函数,来看源码
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } /** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
一个是FutureTask(Callable callbale),接受Callable对象,另一个是FutureTask(Runnable runnable,V result),接受Runnable对象。但是从源码可以看出,不管是Callable或者是Runnable,FutureTask都将其转化成Callable对象,Executors.callable(runnable, result);这个方法使用了适配器模式,将Runnable对象转换成Callable对象,看一眼源码:
public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); } static final class RunnableAdapter<T> implements Callable<T> { final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; } }
从源码可以看出,适配器将Runnable对象的run方法放在了Callable对象的call接口中
也就是说,无论是Callable还是Runnable对象,在FutureTask中都是当做Callable对象使用,由于FutureTask继承了Runnable接口,看一眼其实现的run方法
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
其核心就是执行callable对象的call方法,这也和上面的分析对应。
然后看一眼FutureTask的get方法
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); }
如果计算没有结束,则阻塞,如果已经完成则返回计算结果
说了这么多,最后来看看线程池。
首先看下线程池的简单用法:
package com.luchi.thread.threadpool; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class TestThreadPool implements Callable<Integer>{ @Override public Integer call() throws Exception { // TODO Auto-generated method stub System.out.println("the thread is running"); return 10; } public static void main(String args[]) throws InterruptedException, ExecutionException{ ExecutorService excutor =Executors.newCachedThreadPool(); TestFutureRCallable test=new TestFutureRCallable(); Future<Integer> future=excutor.submit(test); System.out.println(" "+future.get()); excutor.shutdown(); } }
上面的程序中,简单的使用了线程池,常见的获取线程池的方法有两种,一种是 Executors.newCachedThreadPool()一种是Executors.newFixedThreadPool();看一眼两者的源码
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
两者都返回了ThreadPoolExecutor对象,ThreadPoolExecutor构造函数的意义简单解释下,第一个和第二个参数指的是线程池中线程的线程数量最小M和最大的值N,第三个是多长时间空闲线程回收,第四个参数是第三个的时间单位,第五个参数是表示使用的阻塞Queue,线程池开设线程的方法如下:
假如新任务来了,如果当前线程数少于最小的M,则新增一个线程,如果在M~N之间,则把任务丢进等待队列中,如果等待队列满了之后,则再新增一个线程,直到到最大的值N。
newFixecThreadPool中使用了M值和N值相同,也就是新任务来了会一直增开线程数到M,然后再丢进LinkedBlockingQueue中,LinkedBlockingQueue是一个大小无限的阻塞队列,当然这个无限是相对于当前的资源情况,newCachedThreadPool的线程数是从0到无限个,而SynchronousQueue容量为0,意味着任务来了就新开一个线程?(这里不是很了解,有待研究)
再来看一下其submit()方法
public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } /** * @throws RejectedExecutionException {@inheritDoc} * @throws NullPointerException {@inheritDoc} */ public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }
summit接受Callable和Runnable方法,返回执行的Future对象,本文不去探讨实现细节。
相关推荐
滞销楼盘问题小析.doc
小析绩效审计发展态势.doc
光伏并网微逆变器核心技术小析.pdf光伏并网微逆变器核心技术小析.pdf
光伏并网微逆变器核心技术小析.docx光伏并网微逆变器核心技术小析.docx
小析火力发电企业物流成本管理.doc
大学健美操教育的问题与路径小析.doc
美术中的信息化应用小析(全文).docx
渗透测试和安全测试入门相关,扫盲安全测试;安全事,无大小;也许你的一个提醒,可以挽回公司的巨大损失
属性约简是粗糙集理论的核心问题之一,针对求取决策表所有决策约简集的NP问题,化繁为简将问题转化为对象动态增加下的决策约简求取问题。在深入分析了可辨识矩阵中可辨识集的特点及相互关系的基础上,优化改进决策辨识...
石油IC卡在使用中的安全性及其附加价值小析,堵晶晶,,1920年,美国吉尔巴克(GILBARCO)公司生产了世界第一台机械燃油加油机;1974年,推出了世界第一台电脑加油机,有了电脑系统,加油机�
滞销楼盘问题小析DOC简洁、实用的特性,相信能够为大家利用人力、物力、财力、资源等带来许多帮助,欢迎...该文档为滞销楼盘问题小析DOC,是一份很不错的参考资料,具有较高参考价值,感兴趣的可以下载看看
唐人送别诗小析 (2003年)
对小波分析分讲解以及实例教学,可以实现,内附word说明
网站的专场设计,应该算是网页视觉设计师的必修课,应该也算是最基本功。她所需要的设计理论都是最基本,但同时也是最重要的。设计理论版本多如牛毛,我这里仅仅整理5个方面来分析网页专场设计的一些方法,本人并非...
智能家居被看作是下一个风口,但是近两年智能家居市场一直不温不火。一方面,很多智能产品并不能够抓住用户痛点,真正满足用户的需求,很多用户对此的接受度不高。另一方面,这个市场的格局还未形成, 无论是技术...
[原创]uc-OS-II内核小析 - uCOS-II - 电子开开发
论文研究-计算约简的差别矩阵简化算法...实际应用中给出一种简化算法:一边从信息表中提取差别元素构成合取范式,一边用分配律、吸收律作逻辑公式的等价变换,直接得到最小析取范式.本章给出反例,说明该简化算法不总成立.
属性约简是粗糙集理论中重要...在此基础上,构造了一种基于可区分对象集的属性约简算法,算法利用可区分对象集的集对,计算极小析取范式求解出所有的分布约简和最大分布约简。最后,利用实例分析验证了算法的有效性。