线程池学习笔记

Java线程池ThreadPoolExecutor学习笔记java

1:核心线程:简单来说就是线程池中可否容许同时并发运行的线程的数量缓存

2:线程池大小:线程池中最多可以容纳的线程的数量。并发

3:队列:对提交过来的任务的处理模式。异步

public ThreadPoolExecutor(int corePoolSize,  
                          int maximumPoolSize,  
                          long keepAliveTime,  
                          TimeUnit unit,  
                          BlockingQueue<Runnable> workQueue,  
                          ThreadFactory threadFactory,  
                          RejectedExecutionHandler handler)

corePoolSize 核心线程数,指保留的线程池大小(不超过maximumPoolSize值时,线程池中最多有corePoolSize 个线程工做)。 
maximumPoolSize 指的是线程池的最大大小(线程池中最大有corePoolSize 个线程可运行)。 
keepAliveTime 指的是空闲线程结束的超时时间(当一个线程不工做时,过keepAliveTime 长时间将中止该线程)。 
unit 是一个枚举,表示 keepAliveTime 的单位(有NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7个可选值)。 
workQueue 表示存听任务的队列(存放须要被线程池执行的线程队列)。 
handler 拒绝策略(当添加任务数超过maximumPoolSize+workQueue数量时报错).ide

运行时的分配机制学习

1)当池子大小小于corePoolSize就新建线程,并处理请求测试

2)当池子大小等于corePoolSize,把请求放入workQueue中,池子里的空闲线程就去从workQueue中取任务并处理this

3)当workQueue放不下新入的任务时,新建线程入池,并处理请求,若是池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来作拒绝处理atom

4)另外,当池子的线程数大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,若是无请求可处理就自行销毁.net

private ThreadPoolExecutor poolExecutor;
poolExecutor = new ThreadPoolExecutor(3, 10,
                1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(6));

for (int i = 0; i < 30; i++) {
   final int finalI = i;
   Runnable runnable = new Runnable() {
      @Override
      public void run() {
          Log.e("curentThread ","value: " + Thread.currentThread().getName() + "-----" + finalI);
          SystemClock.sleep(3000);
      }
  };
  poolExecutor.execute(runnable);
}

运行时会报错,由于有30个任务须要运行,而缓存队列只能缓存6个,剩下的任务须要去建立新线程,但是线程最多建立10因此就会越界报错

若是new LinkedBlockingDeque<Runnable>()则表示队列为无限大,上面运行结果为

全部的任务都存在队列中,每次只有三个线程运行,因此显示thread-1,2,3

poolExecutor = new ThreadPoolExecutor(3, 30,
                1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(6));

若是,将maximumPoolSize改成30,则会出现20多个线程,由于队列中只存在6个任务,剩下的20多个任务须要去申请新的线程,这个能够本身运行查看效果

固定数量的线程池newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {

    return new ThreadPoolExecutor(nThreads, nThreads,

                                  0L, TimeUnit.MILLISECONDS,

                                  new LinkedBlockingQueue<Runnable>());

}

这个线程池的核心线程与最大线程为一个值,不等待,超出核心线程必定时间后的线程就被回收掉了。最多同时运行nThreads数量的线程。

单线程池

public static ExecutorService newSingleThreadExecutor() {

    return new FinalizableDelegatedExecutorService

        (new ThreadPoolExecutor(1, 1,

                                0L, TimeUnit.MILLISECONDS,

                                new LinkedBlockingQueue<Runnable>()));

}

核心线程为1的固定线程池

 动态线程池(无界线程池)

public static ExecutorService newCachedThreadPool() {

    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                  60L, TimeUnit.SECONDS,

                                  new SynchronousQueue<Runnable>());

}

核心线程池为0,线程池的最大是无限,等待时间为60秒,队列为直接提交。用的是SynchronousQueue队列,没有排队等待直接添加线程。newCachedThreadPool比较适合没有固定大小而且比较快速就能完成的小任务,不必维持一个Pool,这比直接new Thread来处理的好处是能在60秒内重用已建立的线程。机制就是查看已经建立的线程若是有空闲的(60秒之内超过就回收了)能够直接使用,不用从新建立。

线程池虽然队列能够换成无穷多个任务,可是当任务过多时就会致使内存溢出,因此当任务很是多时,要使用有界队列自定义线程池防止oom。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class CustomThreadPoolExecutor {


    private ThreadPoolExecutor pool = null;

    public void init() {
        pool = new ThreadPoolExecutor(
                3,
                10,
                30,
                TimeUnit.MINUTES,
                new ArrayBlockingQueue<Runnable>(5),
                new CustomThreadFactory(),
                new CustomRejectedExecutionHandler());
    }


    public void destory() {
        if(pool != null) {
            pool.shutdownNow();
        }
    }


    public ExecutorService getCustomThreadPoolExecutor() {
        return this.pool;
    }

    private class CustomThreadFactory implements ThreadFactory {

        //原子操做 具体volatile
        private AtomicInteger count = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            String threadName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1);
            System.out.println(threadName);
            t.setName(threadName);
            return t;
        }
    }


    private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                // 核心改造点,由blockingqueue的offer会抛弃任务,改为put阻塞方法,这种不会丢失任务
                executor.getQueue().put(r);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }



    // 测试构造的线程池
    public static void main(String[] args) {

        CustomThreadPoolExecutor exec = new CustomThreadPoolExecutor();
        // 1.初始化
        exec.init();

        ExecutorService pool = exec.getCustomThreadPoolExecutor();
        for(int i=1; i<100; i++) {
            System.out.println("提交第" + i + "个任务!");
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(">>>task is running=====");
                        TimeUnit.SECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

 

LinkedBlockingQueue添加方法put、offer、add介绍http://blog.csdn.net/z69183787/article/details/46986823

AtomicInteger介绍http://haininghacker-foxmail-com.iteye.com/blog/1401346

总结: 
一、用ThreadPoolExecutor自定义线程池,看线程是的用途,若是任务量不大,能够用无界队列,若是任务量很是大,要用有界队列,防止OOM 
二、若是任务量很大,还要求每一个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改成阻塞提交。保证不抛弃一个任务 
三、最大线程数通常设为2N+1最好,N是CPU核数 
四、核心线程数,看应用,若是是任务,一天跑一次,设置为0,合适,由于跑完就停掉了,若是是经常使用线程池,看任务量,是保留一个核心仍是几个核心线程数 
五、若是要获取任务执行结果,用CompletionService,可是注意,获取任务的结果的要从新开一个线程获取,若是在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,因此最好异步开个线程获取结果

参考http://825635381.iteye.com/blog/2184680

相关文章
相关标签/搜索