java高级-高性能-多线程并发编程--1.1.7 线程池原理

线程是不是越多越好

不是

如无必要,尽量嫌少使用线程

  1. 如果线程 创建时间+销毁时间 > 执行时间 着很不划算

  2. 线程 [默认最大栈1M] 会占用内存

  3. 操作系统 频繁的切换上下文 ,非常影响性能

为了 既能保障 同时并发多个线程 有要减少线程数量过多带来的问题,
线程池 给出了解决方案

线程池–是什么

1、线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用
2、可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。
在这里插入图片描述

提交一个任务到线程池中,线程池的处理流程如下:

1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。

2、线程池判断工作队列是否已满(阻塞队列),如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。

3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

总结即:处理任务判断的优先级为 核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

注意:

当workQueue(存放任务的队列)使用的是无界限队列时,maximumPoolSize参数就变的无意义了,比如new LinkedBlockingQueue(),或者new ArrayBlockingQueue(Integer.MAX_VALUE);

使用SynchronousQueue队列时由于该队列没有容量的特性,所以不会对任务进行排队,如果线程池中没有空闲线程,会立即创建一个新线程来接收这个任务。maximumPoolSize要设置大一点。

核心线程和最大线程数量相等时keepAliveTime无作用.

线程池关闭

shutdown() 不接收新任务,会处理已添加任务

shutdownNow() 不接受新任务,不处理已添加任务,中断正在处理的任务

创建

public ThreadPoolExecutor(int corePoolSize,//线程池 核心 线程数量
                  int maximumPoolSize,//线程池最大线程数量
  long keepAliveTime,//当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
                  TimeUnit unit,//存活时间的单位
                 BlockingQueue<Runnable> workQueue,//存放任务的队列
                 RejectedExecutionHandler handler//超出线程范围和队列容量的任务的处理程序超出线程范围和队列容量的任务的处理程序
                 ) 

当线程池任务处理不过来的时候(什么时候认为处理不过来后面描述),可以通过handler指定的策略进行处理,ThreadPoolExecutor提供了四种策略:

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出- RejectedExecutionException异常;也是默认的处理方式。

  • ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。

  • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

可以通过实现RejectedExecutionHandler接口自定义处理方式。

源码代码

ThreadPoolExecutor的execute()方法[执行]

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
       //如果线程数大于等于基本线程数或者线程创建失败,将任务加入队列
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
          //线程池处于运行状态并且加入队列成功
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
         //线程池不处于运行状态或者加入队列失败,则创建线程(创建的是非核心线程)
            else if (!addIfUnderMaximumPoolSize(command))
           //创建线程失败,则采取阻塞处理的方式
                reject(command); // is shutdown or saturated
        }
    }
2、创建线程的方法:addIfUnderCorePoolSize(command)

 
private boolean addIfUnderCorePoolSize(Runnable firstTask) {
        Thread t = null;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (poolSize < corePoolSize && runState == RUNNING)
                t = addThread(firstTask);
        } finally {
            mainLock.unlock();
        }
        if (t == null)
            return false;
        t.start();
        return true;
    }
我们重点来看第7行:

private Thread addThread(Runnable firstTask) {
        Worker w = new Worker(firstTask);
        Thread t = threadFactory.newThread(w);
        if (t != null) {
            w.thread = t;
            workers.add(w);
            int nt = ++poolSize;
            if (nt > largestPoolSize)
                largestPoolSize = nt;
        }
        return t;
    }
这里将线程封装成工作线程worker,并放入工作线程组里,worker类的方法run方法:

public void run() {
            try {
                Runnable task = firstTask;
                firstTask = null;
                while (task != null || (task = getTask()) != null) {
                    runTask(task);
                    task = null;
                }
            } finally {
                workerDone(this);
            }
        }
worker在执行完任务后,还会通过getTask方法循环获取工作队里里的任务来执行。

例子

1、创建一个线程

public class ThreadPoolTest implements Runnable
{
    @Override
    public void run()
{
        try
        {
            Thread.sleep(300);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}
2、线程池循环运行16个线程:

public static void main(String[] args)
{
        LinkedBlockingQueue<Runnable> queue =
            new LinkedBlockingQueue<Runnable>(5);
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, queue);
        for (int i = 0; i < 16 ; i++)
        {
            threadPool.execute(
                new Thread(new ThreadPoolTest(), "Thread".concat(i + "")));
            System.out.println("线程池中活跃的线程数:" + threadPool.getPoolSize());
            if (queue.size() > 0)
            {
                System.out.println("----------------队列中阻塞的线程数" + queue.size());
            }
        }
        threadPool.shutdown();
    }
执行结果:

线程池中活跃的线程数:1
线程池中活跃的线程数:2
线程池中活跃的线程数:3
线程池中活跃的线程数:4
线程池中活跃的线程数:5
线程池中活跃的线程数:5
----------------队列中阻塞的线程数1
线程池中活跃的线程数:5
----------------队列中阻塞的线程数2
线程池中活跃的线程数:5
----------------队列中阻塞的线程数3
线程池中活跃的线程数:5
----------------队列中阻塞的线程数4
线程池中活跃的线程数:5
----------------队列中阻塞的线程数5
线程池中活跃的线程数:6
----------------队列中阻塞的线程数5
线程池中活跃的线程数:7
----------------队列中阻塞的线程数5
线程池中活跃的线程数:8
----------------队列中阻塞的线程数5
线程池中活跃的线程数:9
----------------队列中阻塞的线程数5
线程池中活跃的线程数:10
----------------队列中阻塞的线程数5
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task Thread[Thread15,5,main] rejected from java.util.concurrent.ThreadPoolExecutor@232204a1[Running, pool size = 10, active threads = 10, queued tasks = 5, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
    at test.ThreadTest.main(ThreadTest.java:17)

可以观察出:

1、创建的线程池具体配置为:核心线程数量为5个;全部线程数量为10个;工作队列的长度为5。

2、我们通过queue.size()的方法来获取工作队列中的任务数。

3、运行原理:

刚开始都是在创建新的线程,达到核心线程数量5个后,新的任务进来后不再创建新的线程,而是将任务加入工作队列,任务队列到达上线5个后,新的任务又会创建新的普通线程,直到达到线程池最大的线程数量10个,后面的任务则根据配置的饱和策略来处理。我们这里没有具体配置,使用的是默认的配置AbortPolicy:直接抛出异常。

当然,为了达到我需要的效果,上述线程处理的任务都是利用休眠导致线程没有释放!!!

RejectedExecutionHandler:饱和策略

当队列和线程池都满了,说明线程池处于饱和状态,那么必须对新提交的任务采用一种特殊的策略来进行处理。这个策略默认配置是AbortPolicy,表示无法处理新的任务而抛出异常

JAVA提供了4中策略:

1、AbortPolicy:直接抛出异常

2、CallerRunsPolicy:只用调用所在的线程运行任务

3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。

4、DiscardPolicy:不处理,丢弃掉。

这里采用了丢弃策略后,就没有再抛出异常,而是直接丢弃。在某些重要的场景下,可以采用记录日志或者存储到数据库中,而不应该直接丢弃

API 3个接口定义 和2个实现类[基础]

在这里插入图片描述

ThreadPoolExecutor通过几个核心参数来定义不同类型的线程池,适用于不同的使用场景;其中在任务提交时,会依次判断corePoolSize, workQueque, 及maximumPoolSize,不同的状态不同的处理。

第一个接口ExecutorService

//监测ExecutorService是否已经关闭, 直到所有任务完成执行,或超时发生,或当前线程被中断
await Termination(long timeout, TimeUnit unit)

// 执行给定的任务集合, 执行完毕后,返回结果
invokeAl(Collection<? extends Callable> tasks)

//执行给定的任务集合,执行完毕或者超时后,返回结果,其他任务终止
invokeAll(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)

//执行给定的任务,任意-个执行成功则返回结果,其他任务终止
invokeAny(Collection<? extends Callable> tasks)

//执行给定的任务, 任意个执行成功或者超时后, 则返回结果, 其他任务终止
invokeAny(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)

//如果此线程池已关闭,则返回true。
isShutdown()

//如果关闭后所有任务 都已完成,则返回true。
isTerminated()

// 优雅关闭线程池,之前提交的任务将被执行,但是不会接受新的任务。
shutdown()

//尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行任务的列表。
shutdownNow()

//提交个用于执行的allable返回任务, 并返回个Future,用于获取Callable执行结果
submit(Callable task)

//提交可运行任务以执行,并发回一个Future对象, 执行结果为null
submit(Runnable task)

//提交可运行任务以执行,并返回Future,执行结果为传入的result
submit(Runnable task, T result)

ScheduledExecutorService

1.schedule(Callable callable, long delay, TimeUnit unit)
2.schedule(Runnable command, long delay, TimeUnit unit)

2个都是: 创建并且执行一个一次性任务 ,过了延迟时间 就会执行

3.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

创建执行一个周期性任务
过了给定时间 会执行一次
发生异常 任务停止

当前执行任务时长超过了周期时间,下一次任务等它执行完后马上执行

4.scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
创建执行一个周期性任务

过了给定时间 会执行一次
发生异常 任务停止

当前执行任务时长超过了周期时间,下一次任务会在上个任务结束时间的基础上 ,计算出 执行延时

Executors线程工厂类代码讲解

package com.study.hc.thread.chapter1.thread;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/** 线程池的使用 */
public class Demo9 {

	/**
	 * 测试: 提交15个执行时间需要3秒的任务,看线程池的状况
	 * 
	 * @param threadPoolExecutor 传入不同的线程池,看不同的结果
	 * @throws Exception
	 */
	public void testCommon(ThreadPoolExecutor threadPoolExecutor) throws Exception {
		// 测试: 提交15个执行时间需要3秒的任务,看超过大小的2个,对应的处理情况
		for (int i = 0; i < 15; i++) {
			int n = i;
			threadPoolExecutor.submit(new Runnable() {
				@Override
				public void run() {
					try {
						System.out.println("开始执行:" + n);
						Thread.sleep(3000L);
						System.err.println("执行结束:" + n);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
			System.out.println("任务提交成功 :" + i);
		}
		// 查看线程数量,查看队列等待数量
		Thread.sleep(500L);
		System.out.println("当前线程池线程数量为:" + threadPoolExecutor.getPoolSize());
		System.out.println("当前线程池等待的数量为:" + threadPoolExecutor.getQueue().size());
		// 等待15秒,查看线程数量和队列数量(理论上,会被超出核心线程数量的线程自动销毁)
		Thread.sleep(15000L);
		System.out.println("当前线程池线程数量为:" + threadPoolExecutor.getPoolSize());
		System.out.println("当前线程池等待的数量为:" + threadPoolExecutor.getQueue().size());
	}

	/**
	 * 1、线程池信息: 核心线程数量5,最大数量10,无界队列,超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的
	 * 核心线程数量5,最大数量10 多出的的5个有生命周期 过了时间就销毁了
	 * @throws Exception
	 */
	private void threadPoolExecutorTest1() throws Exception {
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
				new LinkedBlockingQueue<Runnable>());
		testCommon(threadPoolExecutor);
		// 预计结果:线程池线程数量为:5,超出数量的任务,其他的进入队列中等待被执行
	}

	/**
	 * 2、 线程池信息: 核心线程数量5,最大数量10,队列大小3,超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的
	 * 
	 * @throws Exception
	 */
	private void threadPoolExecutorTest2() throws Exception {
		// 创建一个 核心线程数量为5,最大数量为10,等待队列最大是3 的线程池,也就是最大容纳13个任务。
		// 默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
				new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.err.println("有任务被拒绝执行了");
					}
				});
		testCommon(threadPoolExecutor);
		// 预计结果:
		// 1、 5个任务直接分配线程开始执行
		// 2、 3个任务进入等待队列
		// 3、 队列不够用,临时加开5个线程来执行任务(5秒没活干就销毁)
		// 4、 队列和线程池都满了,剩下2个任务,没资源了,被拒绝执行。
		// 5、 任务执行,5秒后,如果无任务可执行,销毁临时创建的5个线程
	}

	/**
	 * 3、 线程池信息: 核心线程数量5,最大数量5,无界队列,超出核心线程数量的线程存活时间:5秒
	 * 
	 * @throws Exception
	 */
	private void threadPoolExecutorTest3() throws Exception {
		// 和Executors.newFixedThreadPool(int nThreads)一样的
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
				new LinkedBlockingQueue<Runnable>());
		testCommon(threadPoolExecutor);
		// 预计结:线程池线程数量为:5,超出数量的任务,其他的进入队列中等待被执行
	}

	/**
	 * 4、 线程池信息:
	 * 核心线程数量0,最大数量Integer.MAX_VALUE,SynchronousQueue队列,超出核心线程数量的线程存活时间:60秒
	 * 
	 * @throws Exception
	 */
	private void threadPoolExecutorTest4() throws Exception {

		// SynchronousQueue,实际上它不是一个真正的队列,因为它不会为队列中元素维护存储空间。与其他队列不同的是,它维护一组线程,这些线程在等待着把元素加入或移出队列。
		// 在使用SynchronousQueue作为工作队列的前提下,客户端代码向线程池提交任务时,
		// 而线程池中又没有空闲的线程能够从SynchronousQueue队列实例中取一个任务,
		// 那么相应的offer方法调用就会失败(即任务没有被存入工作队列)。
		// 此时,ThreadPoolExecutor会新建一个新的工作者线程用于对这个入队列失败的任务进行处理(假设此时线程池的大小还未达到其最大线程池大小maximumPoolSize)。

		// 和Executors.newCachedThreadPool()一样的
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
				new SynchronousQueue<Runnable>());
		testCommon(threadPoolExecutor);
		// 预计结果:
		// 1、 线程池线程数量为:15,超出数量的任务,其他的进入队列中等待被执行
		// 2、 所有任务执行结束,60秒后,如果无任务可执行,所有线程全部被销毁,池的大小恢复为0
		Thread.sleep(60000L);
		System.out.println("60秒后,再看线程池中的数量:" + threadPoolExecutor.getPoolSize());
	}

	/**
	 * 5、 定时执行线程池信息:3秒后执行,一次性任务,到点就执行 <br/>
	 * 核心线程数量5,最大数量Integer.MAX_VALUE,DelayedWorkQueue延时队列,超出核心线程数量的线程存活时间:0秒
	 * 
	 * @throws Exception
	 */
	private void threadPoolExecutorTest5() throws Exception {
		// 和Executors.newScheduledThreadPool()一样的
		ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
		threadPoolExecutor.schedule(new Runnable() {
			@Override
			public void run() {
				System.out.println("任务被执行,现在时间:" + System.currentTimeMillis());
			}
		}, 3000, TimeUnit.MILLISECONDS);
		System.out.println(
				"定时任务,提交成功,时间是:" + System.currentTimeMillis() + ", 当前线程池中线程数量:" + threadPoolExecutor.getPoolSize());
		// 预计结果:任务在3秒后被执行一次
	}

	/**
	 * 6、 定时执行线程池信息:线程固定数量5 ,<br/>
	 * 核心线程数量5,最大数量Integer.MAX_VALUE,DelayedWorkQueue延时队列,超出核心线程数量的线程存活时间:0秒
	 * 
	 * @throws Exception
	 */
	private void threadPoolExecutorTest6() throws Exception {
		ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(5);
		// 周期性执行某一个任务,线程池提供了两种调度方式,这里单独演示一下。测试场景一样。
		// 测试场景:提交的任务需要3秒才能执行完毕。看两种不同调度方式的区别
		// 效果1: 提交后,2秒后开始第一次执行,之后每间隔1秒,固定执行一次(如果发现上次执行还未完毕,则等待完毕,完毕后立刻执行)。
		// 也就是说这个代码中是,3秒钟执行一次(计算方式:每次执行三秒,间隔时间1秒,执行结束后马上开始下一次执行,无需等待)
		//先等待,结束后马上执行 1 , 3 , 5 , 7...
		threadPoolExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(3000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("任务-1 被执行,现在时间:" + System.currentTimeMillis());
			}
		}, 2000, 1000, TimeUnit.MILLISECONDS);

		// 效果2:提交后,2秒后开始第一次执行,之后每间隔1秒,固定执行一次(如果发现上次执行还未完毕,则等待完毕,等上一次执行完毕后再开始计时,等待1秒)。
		// 也就是说这个代码钟的效果看到的是:4秒执行一次。 (计算方式:每次执行3秒,间隔时间1秒,执行完以后再等待1秒,所以是 3+1)
		//先等待,结束后等一个时间执行 1 , 4 , 7 , 11....
		threadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(3000L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("任务-2 被执行,现在时间:" + System.currentTimeMillis());
			}
		}, 2000, 1000, TimeUnit.MILLISECONDS);
	}

	/**
	 * 7、 终止线程:线程池信息: 核心线程数量5,最大数量10,队列大小3,超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的
	 * 
	 * @throws Exception
	 */
	private void threadPoolExecutorTest7() throws Exception {
		// 创建一个 核心线程数量为5,最大数量为10,等待队列最大是3 的线程池,也就是最大容纳13个任务。
		// 默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
				new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.err.println("有任务被拒绝执行了");
					}
				});
		// 测试: 提交15个执行时间需要3秒的任务,看超过大小的2个,对应的处理情况
		for (int i = 0; i < 15; i++) {
			int n = i;
			threadPoolExecutor.submit(new Runnable() {
				@Override
				public void run() {
					try {
						System.out.println("开始执行:" + n);
						Thread.sleep(3000L);
						System.err.println("执行结束:" + n);
					} catch (InterruptedException e) {
						System.out.println("异常:" + e.getMessage());
					}
				}
			});
			System.out.println("任务提交成功 :" + i);
		}
		// 1秒后终止线程池
		Thread.sleep(1000L);
		threadPoolExecutor.shutdown();
		// 再次提交提示失败
		threadPoolExecutor.submit(new Runnable() {
			@Override
			public void run() {
				System.out.println("追加一个任务");
			}
		});
		// 结果分析
		// 1、 10个任务被执行,3个任务进入队列等待,2个任务被拒绝执行
		// 2、调用shutdown后,不接收新的任务,等待13任务执行结束
		// 3、 追加的任务在线程池关闭后,无法再提交,会被拒绝执行
	}

	/**
	 * 8、 立刻终止线程:线程池信息: 核心线程数量5,最大数量10,队列大小3,超出核心线程数量的线程存活时间:5秒, 指定拒绝策略的
	 * 
	 * @throws Exception
	 */
	private void threadPoolExecutorTest8() throws Exception {
		// 创建一个 核心线程数量为5,最大数量为10,等待队列最大是3 的线程池,也就是最大容纳13个任务。
		// 默认的策略是抛出RejectedExecutionException异常,java.util.concurrent.ThreadPoolExecutor.AbortPolicy
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
				new LinkedBlockingQueue<Runnable>(3), new RejectedExecutionHandler() {
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.err.println("有任务被拒绝执行了");
					}
				});
		// 测试: 提交15个执行时间需要3秒的任务,看超过大小的2个,对应的处理情况
		for (int i = 0; i < 15; i++) {
			int n = i;
			threadPoolExecutor.submit(new Runnable() {
				@Override
				public void run() {
					try {
						System.out.println("开始执行:" + n);
						Thread.sleep(3000L);
						System.err.println("执行结束:" + n);
					} catch (InterruptedException e) {
						System.out.println("异常:" + e.getMessage());
					}
				}
			});
			System.out.println("任务提交成功 :" + i);
		}
		// 1秒后终止线程池
		Thread.sleep(1000L);
		List<Runnable> shutdownNow = threadPoolExecutor.shutdownNow();
		// 再次提交提示失败
		threadPoolExecutor.submit(new Runnable() {
			@Override
			public void run() {
				System.out.println("追加一个任务");
			}
		});
		System.out.println("未结束的任务有:" + shutdownNow.size());

		// 结果分析
		// 1、 10个任务被执行,3个任务进入队列等待,2个任务被拒绝执行
		// 2、调用shutdownnow后,队列中的3个线程不再执行,10个线程被终止
		// 3、 追加的任务在线程池关闭后,无法再提交,会被拒绝执行
	}

	public static void main(String[] args) throws Exception {
//		new Demo9().threadPoolExecutorTest1();
//		new Demo9().threadPoolExecutorTest2();
//		new Demo9().threadPoolExecutorTest3();
//		new Demo9().threadPoolExecutorTest4();
//		new Demo9().threadPoolExecutorTest5();
//		new Demo9().threadPoolExecutorTest6();
//		new Demo9().threadPoolExecutorTest7();
		new Demo9().threadPoolExecutorTest8();
	}
}

测试1:问题 当我提交了15个任务时 我发现执行线程为5条 ,等待为10 条及 最大线程数 并未起作用 为什么?

之前介绍了 任务提交的步骤 ;
在这里插入图片描述

测试2:核心5,最大10,队列3 超出核心数量活5秒 ,触发拒绝策略

代码中执行了15个任务
最大容量执行 13 个任务 拒绝 2个

测试3 核心=最大

无界队列存在, 最大线程数 没有多大意义

测试4 核心=0, 直接给到队列

任务多少 不确定 使用
比如代码中 提交了15个
着:当前线程池线程数量:15
当前线程池等待数量:0

//这15个 是加开的 ,到时间就没了 ,但如果 其间 又有人用 着不会再加开线程 而是复用

测试5 定时任务执行 :延时队列

常用队列介绍

  1. ArrayBlockingQueue: 这是一个由数组实现的容量固定的有界阻塞队列.

  2. SynchronousQueue: 没有容量,不能缓存数据;每个put必须等待一个take; offer()的时候如果没有另一个线程在poll()或者take()的话返回false。

  3. LinkedBlockingQueue: 这是一个由单链表实现的默认无界的阻塞队列。LinkedBlockingQueue提供了一个可选有界的构造函数,而在未指明容量时,容量默认为Integer.MAX_VALUE。
    方法------- 说明
    add-------- 增加一个元索; 如果队列已满,则抛出一个异常
    remove— 移除并返回队列头部的元素; 如果队列为空,则抛出一个异常
    offer------- 添加一个元素并返回true; 如果队列已满,则返回false
    poll-------- 移除并返回队列头部的元素; 如果队列为空,则返回null
    put-------- 添加一个元素; 如果队列满,则阻塞
    take------- 移除并返回队列头部的元素; 如果队列为空,则阻塞
    element-- 返回队列头部的元素; 如果队列为空,则抛出一个异常
    peek------ 返回队列头部的元素; 如果队列为空,则返回null

Executors线程工厂类

Executors.newCachedThreadPool();

说明: 创建一个大小无界的可缓存线程池
他的任务队列是一个同步队列,线程空闲60销毁释放 ,线程数随任务的数量变化 ,
核心线程数 = 0 , 最大 线程数为 = Integer.MAX_VALUE

如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
内部实现:new ThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,new SynchronousQueue());

Executors.newFixedThreadPool(int);

说明: 创建一个固定大小,任务容量无界的线程池,可控制线程最大并发数,超出的线程会在队列中等待。
核心线程数=最大线程数
内部实现:new ThreadPoolExecutor(nThreads, nThreads,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue());

Executors.newSingleThreadExecutor();

说明:创建一个单线程化的线程池,它只会用唯一的工作线程来执行无界任务队列,保证所有任务按照顺序执行
内部实现:new ThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue())

单一线程池的池大小 是硬编码 不能再改变

Executors.newScheduledThreadPool(int);

说明:创建一个定长线程池,支持定时及周期性任务执行
内部实现:new ScheduledThreadPoolExecutor(corePoolSize)

如何确定合适的线程数量?

计算型任务: CPU数量的1-2倍

IO型任务 :需要更多的线程,具体根据IO阻塞时长 决定.

tomcat 中默认最大线程数200

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值