java并发编程学习笔记之线程池等源码小析

来源:互联网 发布:酒店软件管理系统 编辑:程序博客网 时间:2024/04/28 05:14

      在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;@Overridepublic Integer call() throws Exception {// TODO Auto-generated method stubSystem.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>{@Overridepublic Integer call() throws Exception {// TODO Auto-generated method stubSystem.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对象,本文不去探讨实现细节。

 

0 0
原创粉丝点击