java线程池详解

线程池

一、什么是线程池?为什么要使用线程池?

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

二、如何实现一个我们自己的线程池

1、线程必须在池子里已经创建好了,并且可以保持住,要有多个容器保存多个线程。
2、线程还要能够接受外部的任务,运行这个任务,容器保持这个来不及运行的任务。
3、实现自己的线程池逻辑,以及使用

package com.demo.thread.myThreadPool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class MyThreadPool {
    //线程池中默认的个数
    private static int WORK_NUM = 5;
    //队列默认任务个数为100
    private static int TASK_COUNT = 100;

    //工作线程组
    private WorkThread[] workThreads;

    //任务队列,作为一个缓冲
    private final BlockingQueue<Runnable> taskQueue;
    private final int work_num;

    //创建具有默认线程个数的线程池
    public MyThreadPool(){
        this(WORK_NUM,TASK_COUNT);
    }

    //创建线程池,worker_num为线程池中工作线程的个数
    public MyThreadPool(int work_num, int taskCount) {
        if (work_num<=0) {
            work_num = WORK_NUM;
        }
        if (taskCount<=0) {
            taskCount = TASK_COUNT;
        }
        this.work_num = work_num;
        taskQueue = new ArrayBlockingQueue<>(taskCount);
        workThreads = new WorkThread[work_num];
        for (int i=0; i<work_num; i++) {
            workThreads[i] = new WorkThread();
            workThreads[i].start();
        }
    }


    //执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器决定
    public void execute(Runnable task) {
        try {
            taskQueue.put(task);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
    public void destroy(){
        //工作线程停止工作,且置为null
        System.out.println("ready close pool......");
        for (int i=0; i<work_num;i++) {
            workThreads[i].stopWorker();
            workThreads[i] = null;//help gc
        }
        taskQueue.clear();//清空任务队列
    }

    //覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数

    @Override
    public String toString() {
        return "WorkThread number:" + work_num + " wait task number:" + taskQueue.size();
    }

    /**
     * 内部类,工作线程
     */
    private class WorkThread extends Thread{
        @Override
        public void run() {
            Runnable r =null;
            try {
                while (!isInterrupted()) {
                    r = taskQueue.take();
                    if (r != null) {
                        System.out.println(getId() + " ready exec :"+r);
                        r.run();
                    }
                    r = null;//help gc
                }
            } catch (Exception e) {
                // TODO: handle execption
            }
        }

        public void stopWorker() {
            interrupt();
        }
    }
}
package com.demo.thread.myThreadPool;

import java.util.Random;

public class TestMyThreadPool {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool t = new MyThreadPool(3,0);
        t.execute(new MyTask("testA"));
        t.execute(new MyTask("testB"));
        t.execute(new MyTask("testC"));
        t.execute(new MyTask("testD"));
        t.execute(new MyTask("testE"));
        System.out.println(t);
        Thread.sleep(10000);
        t.destroy();//所有线程都执行完才destory
        System.out.println(t);
    }

    //任务类
    static class MyTask implements Runnable {
        private String name;
        private Random r = new Random();

        public MyTask(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        @Override
        public void run() {//执行任务
            try {
                Thread.sleep(r.nextInt(1000) + 2000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getId() + " sleep Interrup " + Thread.currentThread().isInterrupted());
            }
            System.out.println("任务" + name + " 完成");
        }
    }
}

三、JDK中线程池以及工作机制

  • 线程池的创建

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

  • 各个参数的含义

int corePoolSize:线程池中核心线程数(如果核心线程数 < corePoolSize,就会创建新线程;如果核心线程数 = corePoolSize,这个任务就会保存到BlockingQueue,如果调用prestartAllCoreThreads()方法就会一次性的启动 corePoolSize 个线程 )
int maximumPoolSize:允许的最大线程数,BlockingQueue也满了,总的线程数 < maximumPoolSize时候,就会再次创建新的线程。
long keepAliveTime:线程空闲下来后,存活的时间,这个参数只在 总的线程数 > corePoolSize 才有用。
TimeUnit unit:存活时间的单位值。
BlockingQueue workQueue:保存任务的阻塞队列。(队列选择可以参考:java阻塞队列的详解
ThreadFactory threadFactory:创建线程的工厂,给新建的线程赋予名字。
RejectedExecutionHandler handler:饱和策略。

  • 饱和策略
    1、AbortPolicy:直接抛出异常(默认)。
    2、CallerRunsPolicy:用调用者所在的线程来执行任务。
    3、DiscardOldestPolicy:丢弃阻塞队列里最老的任务,队列里最靠前的任务。
    4、DiscardPolicy:当前任务直接丢弃。
    如果要实现自己的饱和策略,实现RejectedExecutionHandler 接口即可。

  • 提交任务

execute(Runnable command)	//不需要返回值
Future<> submit(Callable<T> task)	//需要返回值
  • 关闭线程池
    shutdown() 和 shutdownNow()
shutdown()	//设置线程池的状态,还会尝试停止正在运行或者暂停任务的线程
shutdownNow()	//设置线程池的状态,只会中断所有没有执行任务的线程
  • 工作机制
    在这里插入图片描述
    线程池工作机制图
  • 合理配置线程池
    根据任务的性质来:计算类型(CPU),IO密集型,混合型。
    • 计算密集型:加密,大数分解,正则…,线程数适当小一点,最大推荐:机器的CPU核心数+1。为啥+1,就是为了防止页缺失。(机器的CPU核心 = Runtime.getRuntime().availableProcessors(); )
    • IO密集型:读取文件,数据库连接,网络通讯,线程数适当大一点,机器的CPU核心数 * 2。(具体多少?需要生产上看应用的用户态和核心态,如果用户态是100%,线程数就刚刚好。)
    • 混合型:IO密集型 ≈ 计算密集型,尽量拆分。如果IO密集型 >> 计算密集型,拆分意义不大。
    • 线程池的阻塞队列,应该使用有界队列,无界队列可能会导致内存溢出,OOM。

四、预定义的线程池

  • FixedThreadPool:创建固定线程数量,适用于负载较重的服务器,使用了无界队列。
  • SingleThreadExecutor:创建单个线程,需要顺序保证执行任务,不会有多个线程活动,使用了无界队列。
  • CachedThreadPool:会根据需要来创建新线程,执行很多短期异步任务的程序,使用了SynchronousQueue。
  • WorkStealingPool(JDK7以后):基于ForkJoinPool实现。
  • ScheduledThreadPoolExecutor:需要定期执行周期任务,不建议使用Timer。
    • newSingleThreadScheduledExecutor:只包含一个线程,只需要单个线程执行周期任务,应用于保证顺序的执行各个任务。
    • newScheduledThreadPool:可以包含多个线程,线程执行周期任务,适度控制后台线程数量的时候。
    • 方法说明:
      1、schedule:只执行一次,任务还可以延时执行。
      2、scheduleAtFixedRate:提交固定时间间隔的任务。(从前一个任务开始的时间,到下一个任务开始的时间算)
      3、scheduleWithFixedDelay:提交固定延时间隔执行的任务。(从前一个任务的结束时间到下一个任务的开始时间算)

Executor框架
在这里插入图片描述

五、线程池的使用

  • 线程池的使用代码案列
package com.demo.thread.myThreadPool;

import com.demo.tools.SleepTools;

import java.util.Random;
import java.util.concurrent.*;

/**
 * 线程池的使用
 */
public class UseThreadPool {
    static class Worker implements Runnable
    {
        private String taskName;
        private Random r = new Random();

        public Worker(String taskName) {
            this.taskName = taskName;
        }

        public String getName() {
            return taskName;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " process the task : " + taskName);
            SleepTools.ms(r.nextInt(100)*5);
        }
    }

    static class CallWorker implements Callable<String>
    {
        private String taskName;
        private Random r = new Random();

        public CallWorker(String taskName) {
            this.taskName = taskName;
        }

        public String getName() {
            return taskName;
        }

        @Override
        public String call() throws Exception {
            System.out.println(Thread.currentThread().getName() + " process the task : " + taskName);
            return Thread.currentThread().getName()+" : "+r.nextInt(100)*5;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = new ThreadPoolExecutor(2,4,3, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(10),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        /**
         * 和上面的线程池有啥不一样?
         */
        //ExecutorService pool = Executors.newCachedThreadPool();
        for (int i=0;i<6;i++) {
            Worker worker = new Worker("worker_"+i);
            pool.execute(worker);
        }
        for (int i=0;i<6;i++) {
            CallWorker callWorker = new CallWorker("callWorker_"+i);
            Future<String> result = pool.submit(callWorker);
            System.out.println(result.get());
        }
        pool.shutdown();
    }
}

  • 定时任务工作类的异常处理
package com.demo.thread.myThreadPool.schd;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 定时任务的工作类
 */
public class ScheduleWorker implements Runnable {
    public final static int Normal = 0;//普通类型任务
    public final static int HasException = -1;//会抛出异常的任务类型
    public final static int ProcessException = 1;//抛出异常但会捕获的任务类型

    public static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private int taskType;
    public ScheduleWorker(int taskType) {
        this.taskType = taskType;
    }

    @Override
    public void run() {
        if (taskType==HasException) {
            System.out.println(format.format(new Date()) + " Exception made...");
            throw new RuntimeException("HasException Happen");
        } else if (taskType==ProcessException) {
            try {
                System.out.println(format.format(new Date()) + " Exception made, but catch");
            } catch (Exception e) {
                System.out.println("Exception be catched");
            }
        } else {
            System.out.println("Normal ... "+ format.format(new Date()));
        }
    }
}

package com.demo.thread.myThreadPool.schd;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 演示 ScheduledThreadPoolExecutor 的用法
 */
public class ScheduledCase {
    public static void main(String[] args) {
        ScheduledThreadPoolExecutor schedule = new ScheduledThreadPoolExecutor(1);
        schedule.scheduleAtFixedRate(new ScheduleWorker(ScheduleWorker.Normal),
                1000,3000, TimeUnit.MILLISECONDS);
        schedule.scheduleAtFixedRate(new ScheduleWorker(ScheduleWorker.HasException),
                1000,3000, TimeUnit.MILLISECONDS);
    }
}

  • 通过CompletionService来实现获取线程池中任务返回的结果
package com.demo.thread.myThreadPool.comps;

import java.util.Random;
import java.util.concurrent.Callable;

/**
 * 任务类
 */
public class WorkTask implements Callable<Integer> {
    private String name;

    public WorkTask(String name) {
        this.name = name;
    }

    @Override
    public Integer call() throws Exception {
        int sleepTime = new Random().nextInt(1000);
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //返回给调用者的值
        return sleepTime;
    }
}

package com.demo.thread.myThreadPool.comps;

import com.demo.tools.SleepTools;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 获取线程池中任务返回的结果
 */
public class CompletionCase {
    private final int POOL_SIZE = Runtime.getRuntime().availableProcessors();
    private final int TOTAL_TASK = Runtime.getRuntime().availableProcessors()*10;

    //方法一,自己写集合来实现获取线程池中任务的返回结果
    public void testByQueue() throws Exception {
        long start = System.currentTimeMillis();
        AtomicInteger count = new AtomicInteger(0);

        //创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);
        //容器存放提交给线程池的任务,list,map
        BlockingQueue<Future<Integer>> queue = new LinkedBlockingQueue<Future<Integer>>();
        //向里面扔任务
        for (int i=0; i<TOTAL_TASK; i++) {
            Future<Integer> future = pool.submit(new WorkTask("ExecTask"+i));
            queue.add(future);
        }
        //检查线程池任务执行结果
        for (int i=0; i<TOTAL_TASK; i++) {
            int sleepTime = queue.take().get();
            System.out.println(" sleep "+sleepTime+" ms ...");
            count.addAndGet(sleepTime);
        }

        //关闭线程池
        pool.shutdown();
        System.out.println("-------tasks sleep time " + count.get()
            + "ms,and spend time "
            +(System.currentTimeMillis()-start)+" ms");
    }

    //方法二,通过CompletionService来实现获取线程池中任务返回的结果
    public void testByCompletion() throws Exception {
        long start = System.currentTimeMillis();
        AtomicInteger count = new AtomicInteger(0);
        //创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);
        CompletionService<Integer> cService = new ExecutorCompletionService<>(pool);

        //向里面仍任务
        for (int i=0; i<TOTAL_TASK; i++) {
            cService.submit(new WorkTask("ExecTask"+i));
        }

        //检查线程池任务执行结果
        for (int i=0; i<TOTAL_TASK; i++) {
            int sleepTime = cService.take().get();
            System.out.println(" sleep "+ sleepTime +" ms ...");
            count.addAndGet(sleepTime);
        }

        //关闭线程池
        pool.shutdown();
        System.out.println("-------tasks sleep time " + count.get()
                + "ms,and spend time "
                +(System.currentTimeMillis()-start)+" ms");
    }

    public static void main(String[] args) throws Exception {
        CompletionCase t = new CompletionCase();
        //t.testByQueue();
        t.testByCompletion();
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值