Why 线程池?
1. 减少每次创建和销毁线程的开销
2. 控制系统中运行的线程数目
首先需要了解ThreadPoolExecutor构造函数,见名知意
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
JAVA自带的有哪些?什么区别?怎么用?
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by itworker365 on 6/2/2017. */ public class ThreadpoolTest { public static void main (String[] args) { //单个线程的线程池,只有一个线程工作,任务相当于串行按照提交顺序执行,挂了就再来一个线程 /* * new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()) */ ExecutorService pool = Executors.newSingleThreadExecutor(); //固定大小的线程池,线程没提交一个任务就会创建一个线程,直到线程达到线程池的大小就不变了,挂了就补一个 /* * ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()) */ // ExecutorService pool = Executors.newFixedThreadPool(3); //无界线程池,可以自动回收,线程多于任务(60S)会减少线程,少于任务会增加线程,不限制线程数量,线程池大小依赖于JVM限制 /* * ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()) */ // ExecutorService pool = Executors.newCachedThreadPool(); //无限大小的线程池,支持周期和定时任务 // ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1); // exec.scheduleAtFixedRate(new Runnable() { // @Override // public void run() { // System.out.println(System.nanoTime()); // } // }, 1000, 2000, TimeUnit.MILLISECONDS); Thread t1 = new TestThread(1); Thread t2 = new TestThread(2); Thread t3 = new TestThread(3); Thread t4 = new TestThread(4); Thread t5 = new TestThread(5); pool.execute(t1); pool.execute(t2); pool.execute(t3); pool.execute(t4); pool.execute(t5); pool.shutdown(); } } class TestThread extends Thread { int id = 0; public TestThread(int threadid) { this.id = threadid; } @Override public void run () { System.out.println(Thread.currentThread().getName() + " current running..." + id); } }
来了一个任务怎么办?
如果运行的线程少于 corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行)
如果运行的线程等于或多于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。
如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
任务队列有何不同?
SynchronousQueue: 直接提交,不存在可用于立即运行任务的线程,则试图加入队列失败,构造一个新线程。参考上面代码 Integer.MAX_VALUE
比如coresize 2, maxcoresize 3,如果已经有两个任务在运行,又新来一个,此时运行线程数已经=2,所以新请求加入队列,再来一个新任务,目前2个在运行,一个在队列,无法加入,只能创建一个新线程来运行,如果再来两个,线程数达到max就要执行拒绝策略了,所以建议无界
LinkedBlockingQueue: 无界队列,因为无界,所以所有大于corePoolSize线程都忙时新任务在队列中等待,所以创建的线程数目就不会超过corePoolSize.
ArrayBlocingQueue: 有界队列,防止资源耗尽,使用大队列和小型池能降低CPU使用,操作系统资源和上下文切换等开销但也降低了吞吐量,反之则资源使用量高,需要结合实际情况综合考虑
队列放不下,线程数已经达到最大,这时候需要执行拒绝策略:
CallerRunsPolicy:线程调用运行该任务的 execute 本身
AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException
DiscardPolicy:不能执行的任务将被删除
DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)
如何实现一个简单的线程池?
主要是两个队列,一个任务队列,一个工作者队列,都是线程
线程池初始化时根据参数构造一定量的工作者线程,并启动,这是工作者线程检查任务队列为空则wait等待
一旦客户端提交任务到线程池,会加入到任务队列并notify工作线程执行对应线程
根据队列的不同线程排队方式不同
import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; /** * Created by itworker365 on 5/10/2017. */ public class SimpleThreadPool<Job extends Runnable>{ // 最大worker数 private static final int MAX_WORKERS = 10; // 默认worker数 private static final int DEFAULT_WORKERS = 5; // 需要执行的任务队列 private final LinkedList<Job> jobs = new LinkedList<Job>(); // 工作者线程的列表 private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>()); // 工作者线程的数量 private int workerNum; //初始化一定数目的worker thread,任务队列为空时wait()等待唤醒 public SimpleThreadPool() { this.workerNum = DEFAULT_WORKERS; initializeWorkers(this.workerNum); } public SimpleThreadPool(int num) { if (num > MAX_WORKERS) { this.workerNum = DEFAULT_WORKERS; } else { this.workerNum = num; } initializeWorkers(this.workerNum); } //初始化工作者线程 private void initializeWorkers(int num) { for (int i = 0; i < num; i++) { Worker worker = new Worker(); //添加到工作者线程的列表 workers.add(worker); //启动工作者线程 Thread thread = new Thread(worker); thread.start(); } } //有任务进来添加到任务队列并Notify()worker线程 public void execute(Job job) { if (job != null) { synchronized (jobs) { jobs.addLast(job); jobs.notify(); } } } //关闭线程池即关闭每个工作者线程 public void shutdown() { for (Worker w : workers) { w.shutdown(); } } //增加工作者线程 public void addWorkers(int num) { //加锁,防止该线程还么增加完成而下个线程继续增加导致工作者线程超过最大值 synchronized (jobs) { if (num + this.workerNum > MAX_WORKERS) { num = MAX_WORKERS - this.workerNum; } initializeWorkers(num); this.workerNum += num; } } //减少工作者线程 public void removeWorker(int num) { synchronized (jobs) { if(num>=this.workerNum){ throw new IllegalArgumentException("超过了已有的线程数量"); } for (int i = 0; i < num; i++) { Worker worker = workers.get(i); if (worker != null) { worker.shutdown(); workers.remove(i); } } this.workerNum -= num; } } //工作者线程,没有任务执行时wait() class Worker implements Runnable { private volatile boolean running = true; public void run() { while (running) { Job job = null; synchronized (jobs) { if (jobs.isEmpty()) { try { jobs.wait(); } catch (InterruptedException e) { //感知到外部对该线程的中断操作,返回 Thread.currentThread().interrupt(); return; } } // 取出一个job job = jobs.removeFirst(); } //执行job if (job != null) { job.run(); } } } public void shutdown() { running = false; } } public static void main (String[] args) { SimpleThreadPool pool = new SimpleThreadPool(3); pool.execute(new Runnable() { @Override public void run() { System.out.println("This1 is started"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("This1 is finished"); } }); pool.execute(new Runnable() { @Override public void run() { System.out.println("This2 is started"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("This2 is finished"); } }); pool.execute(new Runnable() { @Override public void run() { System.out.println("This3 is started"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("This3 is finished"); } }); } }