线程池

什么是线程池?为什么要用线程池?

  1. 降低资源的消耗。降低线程创建和销毁的资源消耗;
  2. 提高响应速度:线程的创建时间为T1,执行时间T2,销毁时间T3,免去T1和T3的时间
  3. 提高线程的可管理性。

JDK中的线程池和工作机制

线程池的创建

ThreadPoolExecutor,jdk所有线程池实现的父类

各个参数含义

int corePoolSize  :线程池中核心线程数,< corePoolSize  ,就会创建新线程,= corePoolSize  ,这个任务就会保存到BlockingQueue,如果调用prestartAllCoreThreads()方法就会一次性的启动corePoolSize  个数的线程。

int maximumPoolSize, 允许的最大线程数,BlockingQueue也满了,< maximumPoolSize时候就会再次创建新的线程

long keepAliveTime, 线程空闲下来后,存活的时间,这个参数只在> corePoolSize才有用

TimeUnit unit, 存活时间的单位值

BlockingQueue workQueue, 保存任务的阻塞队列

ThreadFactory threadFactory, 创建线程的工厂,给新建的线程赋予名字

RejectedExecutionHandler handler :饱和策略

  1.           AbortPolicy :直接抛出异常,默认;
  2.          CallerRunsPolicy:用调用者所在的线程来执行任务
  3.          DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务
  4.          DiscardPolicy :当前任务直接丢弃

 提交任务

execute(Runnable command)  不需要返回

Future submit(Callable task) 需要返回

关闭线程池

shutdown(),shutdownNow();

shutdownNow():设置线程池的状态,还会尝试停止正在运行或者暂停任务的线程

shutdown()设置线程池的状态,只会中断所有没有执行任务的线程

工作机制

线程池_第1张图片

  1. 当线程数
  2. 当线程数=corePoolSize ,想BlockingQueue中添加任务
  3. 当BlockingQueue已满,并且线程数
  4. 当BlockingQueue已满,并且线程数=maxsize,执行饱和策略

 预定义的线程池

FixedThreadPool

ExecutorService pool = Executors.newFixedThreadPool(5);

创建固定线程数量的,适用于负载较重的服务器,使用了无界队列,

缺点:使用了无界队列,当任务过多时可能会导致OOM

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }
public class PoolTest {
    public static void main(String[] args) throws InterruptedException {
//        ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 3000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(10), new ThreadPoolExecutor.DiscardOldestPolicy());
        ExecutorService pool = Executors.newFixedThreadPool(5);
//        ExecutorService pool = Executors.newSingleThreadExecutor();
//        ExecutorService pool = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
            pool.execute(new MyWork(i));
        }

        for (int i = 0; i < 15; i++){
            Thread.sleep(500);
            System.out.println(System.currentTimeMillis());
        }
    }

    static class MyWork implements Runnable {
        int num;
        MyWork(int num){
            this.num = num;
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " is working "+num);
            try {
                Thread.sleep(500);
                System.out.println(Thread.currentThread().getName() + " is done "+num);
            } catch (Exception e) {

            }
        }
    }
}

运行结果:

pool-1-thread-1 is working 0
pool-1-thread-2 is working 1
pool-1-thread-3 is working 2
pool-1-thread-4 is working 3
pool-1-thread-5 is working 4
1591088996392
pool-1-thread-1 is done 0
pool-1-thread-1 is working 5
pool-1-thread-2 is done 1
pool-1-thread-3 is done 2
pool-1-thread-3 is working 7
pool-1-thread-2 is working 6
pool-1-thread-4 is done 3
pool-1-thread-4 is working 8
pool-1-thread-5 is done 4
pool-1-thread-5 is working 9
1591088996892
pool-1-thread-1 is done 5
pool-1-thread-3 is done 7
pool-1-thread-2 is done 6
pool-1-thread-4 is done 8
pool-1-thread-5 is done 9
1591088997392
1591088997892
1591088998392
1591088998892
1591088999392

SingleThreadExecutor

ExecutorService pool = Executors.newSingleThreadExecutor();

创建单个线程,需要顺序保证执行任务,不会有多个线程活动,使用了无界队列

缺点:使用了无界队列,当任务过多时可能会导致OOM

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }

运行结果:

pool-1-thread-1 is working 0
1591089156741
pool-1-thread-1 is done 0
pool-1-thread-1 is working 1
1591089157249
pool-1-thread-1 is done 1
pool-1-thread-1 is working 2
1591089157750
pool-1-thread-1 is done 2
pool-1-thread-1 is working 3
1591089158250
pool-1-thread-1 is done 3
pool-1-thread-1 is working 4
pool-1-thread-1 is done 4
1591089158750
pool-1-thread-1 is working 5
1591089159250
pool-1-thread-1 is done 5
pool-1-thread-1 is working 6
pool-1-thread-1 is done 6
1591089159750
pool-1-thread-1 is working 7
1591089160250
pool-1-thread-1 is done 7
pool-1-thread-1 is working 8
pool-1-thread-1 is done 8
1591089160750
pool-1-thread-1 is working 9
1591089161250
pool-1-thread-1 is done 9
1591089161750
1591089162250
1591089162750

CachedThreadPool

ExecutorService pool = Executors.newCachedThreadPool();

会根据需要来创建新线程的,执行很多短期异步任务的程序,使用了SynchronousQueu

缺点:max设置为Integer.MAX,则如果线程数过多,会导致崩溃

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

运行结果:

pool-1-thread-1 is working 0
pool-1-thread-2 is working 1
pool-1-thread-5 is working 4
pool-1-thread-6 is working 5
pool-1-thread-4 is working 3
pool-1-thread-8 is working 7
pool-1-thread-7 is working 6
pool-1-thread-9 is working 8
pool-1-thread-10 is working 9
pool-1-thread-3 is working 2
pool-1-thread-1 is done 0
1591089242278
pool-1-thread-2 is done 1
pool-1-thread-4 is done 3
pool-1-thread-5 is done 4
pool-1-thread-6 is done 5
pool-1-thread-8 is done 7
pool-1-thread-7 is done 6
pool-1-thread-9 is done 8
pool-1-thread-3 is done 2
pool-1-thread-10 is done 9
1591089242778
1591089243278
1591089243778

WorkStealingPool

基于ForkJoinPool实现

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor service = new ScheduledThreadPoolExecutor(1);

需要定期执行周期任务,Timer不建议使用了。

newSingleThreadScheduledExecutor:只包含一个线程,只需要单个线程执行周期任务,保证顺序的执行各个任务

newScheduledThreadPool 可以包含多个线程的,线程执行周期任务,适度控制后台线程数量的时候

方法说明:

schedule:只执行一次,任务还可以延时执行

scheduleAtFixedRate:提交固定时间间隔的任务

scheduleWithFixedDelay:提交固定延时间隔执行的任务

scheduleAtFixedRate & scheduleWithFixedDelay 区别

线程池_第2张图片

 

scheduleAtFixedRate任务超时:

规定60s执行一次,有任务执行了80S,下个任务马上开始执行

第一个任务 时长 80s,第二个任务20s,第三个任务 50s

第一个任务第0s开始,第80S结束;

第二个任务第80s开始,在第100秒结束;

第三个任务第120s秒开始,170秒结束

第四个任务从180s开始

注意:当执行线程方法时,出现异常,会导致整个ScheduleTask异常,无法继续按期执行,建议在执行方法中try/catch异常

public class SchedulePoolTest {
    public static void main(String[] args) throws InterruptedException {
        ScheduledThreadPoolExecutor service = new ScheduledThreadPoolExecutor(1);
        service.scheduleAtFixedRate(new MyWork(),0,6000, TimeUnit.MILLISECONDS);

        for (int i = 0; i < 15; i++){
            Thread.sleep(1000);
            System.out.println(System.currentTimeMillis());
        }
    }

    static class MyWork implements Runnable{
        @Override
        public void run() {
            System.out.println("I am working");
        }
    }
}

执行结果:

I am working
1591090034596
1591090035596
1591090036596
1591090037596
1591090038596
1591090039596
I am working
1591090040596
1591090041596
1591090042596
1591090043597
1591090044597
1591090045597
I am working
1591090046597

合理配置线程池

根据任务的性质来:计算密集型(CPU),IO密集型混合型

计算密集型:加密,大数分解,正则……., 线程数适当小一点,最大推荐:机器的Cpu核心数+1,为什么+1,防止页缺失,(机器的Cpu核心=Runtime.getRuntime().availableProcessors();)

IO密集型:读取文件,数据库连接,网络通讯, 线程数适当大一点,机器的Cpu核心数*2,

混合型:尽量拆分,IO密集型>>计算密集型,拆分意义不大,IO密集型~计算密集型

队列的选择上,应该使用有界,无界队列可能会导致内存溢出,OOM

CompletionService

/**
 * CompletionServcie的作用:
 * 当向线程池中添加多个任务时,可以根据各个任务的结束时间而获得结果,
 * 不会因为任务加入线程池顺序,而导致先执行完成的任务不能获取到结果
 */

使用方式

ExecutorService service = Executors.newScheduledThreadPool(coreSize);
CompletionService cService = new ExecutorCompletionService(service);

..

cService.submit(..)

cService.take().get();

示例:

public class CompletionServiceTest {
    private static Integer coreSize = Runtime.getRuntime().availableProcessors();
    private static Integer taskSiz = Runtime.getRuntime().availableProcessors();

    static class MyCallable implements Callable {
        private String name;

        MyCallable(String name) {
            this.name = name;
        }

        @Override
        public Long call() throws Exception {
            long sleep = new Random().nextInt(500);
            Thread.sleep(sleep);
            System.out.println(name + " sleep " + sleep + "ms");
            return sleep;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test();
        completionServiceTest();
    }

    private static void test() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ExecutorService service = Executors.newScheduledThreadPool(coreSize);
        BlockingQueue> queue = new LinkedBlockingQueue<>(taskSiz);
        AtomicLong count = new AtomicLong();
        for (int i = 0; i < taskSiz; i++) {
            Future future = service.submit(new MyCallable("task" + i));
            queue.add(future);
            Thread.sleep(100);
        }
        for (Future future : queue) {
            long sleep = queue.take().get();
            count.addAndGet(sleep);
        }
        service.shutdown();
        System.out.println("------------all sleep:" + count.get() + ", all time is:" + (System.currentTimeMillis() - start));
    }

    private static void completionServiceTest() throws InterruptedException, ExecutionException {
        long start = System.currentTimeMillis();
        ExecutorService service = Executors.newScheduledThreadPool(coreSize);
        CompletionService cService = new ExecutorCompletionService(service);
        AtomicLong count = new AtomicLong();
        for (int i = 0; i < taskSiz; i++) {
            cService.submit(new MyCallable("task" + i));
            Thread.sleep(100);
        }
        // 检查线程池任务执行结果
        for (int i = 0; i < taskSiz; i++) {
            long sleptTime = cService.take().get();
            count.addAndGet(sleptTime);
        }
        service.shutdown();
        System.out.println("------------all sleep:" + count.get() + ", all time is:" + (System.currentTimeMillis() - start));
    }

}

运行结果:

task0 sleep 374ms
task1 sleep 454ms
task2 sleep 383ms
task3 sleep 358ms
------------all sleep:1569, all time is:676
task0 sleep 60ms
task1 sleep 102ms
task2 sleep 311ms
task3 sleep 332ms
------------all sleep:805, all time is:635

根据结果可以看出,completionService是根据运行时间获得结果,不根据插入线程池的先后顺序。

你可能感兴趣的:(并发编程,java,多线程)