一、概念
1、并行(parallellism)和并发(concurrency)
QQ和微信同时运行,是并行。在微信上和多个人聊天,是并发。
并行是指同一时刻同时做多件事情,并发是指同一时间间隔内做多件事情。
并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。
2、进程(process)和线程(thread)
线程状态:
线程方法join(),wait(),sleep(),yield()区别:
sleep 和wait比较:1.sleep来之Thread,wait来之Object; 2.sleep不释放锁,wait释放锁; 3.sleep不让出系统资源,wait让出系统资源进入线程等待池,需要用notify/notifyall唤醒才能进入就绪; 4.wait只能在同步代码块/方法中用,sleep可以用于任何地方。
sleep和yield:1.yield暂时交出cpu控制权,从running状态转入runnable状态,扔有可能被调度。sleep是进入阻塞状态,时间到即可运行。2.sleep会给其他线程运行机会,不管优先级,yield只给优先级比自己高或相同的线程机会。
sleep:使线程休眠一段时间,时间结束后进入可执行状态,等待调度执行。休眠期间不释放锁
wait:是线程休眠一段时间,若设置参数,时间到自动进入可执行状态,若没有设置参数,需要用notify/notifyall唤醒,wait进入线程等待池,释放锁。
join:当前运行线程可以调用另一个线程的join,当前线程转入阻塞,直到另一个线程运行结束,它才恢复运行。
yield:线程进入就绪状态,不释放锁。
多线程实现方式:
1.继承Thread类(单继承)
package com.thread.create;
/**
* 模拟龟兔赛跑
* 1.创建多线程:继承Thread
* 2.重写run
* @author chen
*
*/
public class TestThread {
public static void main(String[] args) {
Rabbit rabbit = new Rabbit();
Tortoise tortoise = new Tortoise();
rabbit.start();
tortoise.start();
}
}
class Rabbit extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++) {
System.out.println("兔子跑"+i+"步");
}
}
}
class Tortoise extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++) {
System.out.println("乌龟跑"+i+"步");
}
}
}
2.实现runnalbe接口(多实现,使用静态代理模式)
package com.thread.create;
/**
* 推荐使用runnable接口创建多线程
* 1.避免单线程局限性
* 2.便于共享资源
*
* 使用runnable 创建线程
* 1.类实现runnable接口 +重写run()方法 -->静态代理中的真实角色类
* 2.启动多线程,使用静态代理
* 1)创建真实角色
* 2)创建代理角色+真实角色引用
* 3)调用.start() 启动多线程
* @author chen
*
*/
public class TestRunnable {
public static void main(String[] args) {
// 1)创建真实角色
Programmer pro = new Programmer();
// 2)创建代理角色+真实角色引用
Thread myproxy = new Thread(pro);
// 3)调用.start() 启动多线程
myproxy.start();
//第二条线程
for (int i=0;i<100;i++) {
System.out.println("一边聊天。。。。。。");
}
//测试共享资源
new Thread(new Web12306(),"黄牛1").start();
new Thread(new Web12306(),"黄牛2").start();
}
}
class Programmer implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++) {
System.out.println("一边敲代码。。。。。。");
}
}
}
//方便共享资源实例
class Web12306 implements Runnable{
private int num = 50;
@Override
public void run() {
while(true) {
if (num<=0) {
break;//跳出循环
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
}
3.实现callable接口:
package com.thread.create;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 使用callable实现多线程
* 优点:可以获取返回值,可以对外抛异常(runnable只能try,catch)
* 缺点:创建线程比较繁琐
* 1.创建callable实现类+重写call()
* 2.借助执行任务调度服务ExecutorService获取Futrue对象
* ExecutorService service = Executors.newFixedThreadPool(2);
Future result = service.submit(实现类对象);
3.获取result.get()
4.停止服务service.shutdownNow();
* 模拟龟兔晒跑
* @author chen
*
*/
public class TestCallable {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Race tortoise = new Race("乌龟",100);
Race rabblit = new Race("兔子",1);
//创建线程
ExecutorService service = Executors.newFixedThreadPool(2);
//获取值
Future resulTortoise = service.submit(tortoise);
Future resultRabbit = service.submit(rabblit);
Thread.sleep(5000); //设置跑多少秒
tortoise.setFlag(false);
rabblit.setFlag(false);
int tortoiseNum= (int) resulTortoise.get();
int rabbitNum= (int) resultRabbit.get();
System.out.println("乌龟跑了:"+tortoiseNum);
System.out.println("兔子跑了:"+rabbitNum);
//停止服务
service.shutdownNow();
}
}
class Race implements Callable<Integer>{
private String name;
private long time;
private boolean flag = true;
private int step = 0;
public Race(String name) {
super();
this.name = name;
}
public Race(String name, int time) {
super();
this.name = name;
this.time = time;
}
public Race() {
}
@Override
public Integer call() throws Exception {
while(flag) {
Thread.sleep(time); //延时
step++;
}
return step;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getTime() {
return time;
}
public void setTime(int time) {
this.time = time;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
}
线程info方法:
package com.thread.create;
/**
* 1.isAlive()判断线程是否还活着
* 2.getPriority()获得线程优先级
* 3.setPriority()设置线程优先级
* 4.getName()获得线程名字
* 5.currentThread()获取当前运行的进程
*
* @author chen
*
*/
public class ThreadInfo {
public static void main(String[] args) throws InterruptedException {
myThread t1 = new myThread();
Thread proxy = new Thread(t1,"线程1");
proxy.start();
Thread.sleep(100);
t1.stop();
}
}
class myThread implements Runnable{
private boolean flag = true;
private int num = 0;
@Override
public void run() {
while(flag) {
System.out.println(Thread.currentThread().getName()+"-->"+ num++);
}
}
public void stop() {
this.flag = false;
}
}
线程同步:确保并发线程安全(内存安装)synchronized关键字(同步块,同步方法)
难点:锁定范围过大影响性能,过小可能达不到效果。
过多同步可能造成死锁。
生产者消费者问题(有限缓存问题):解决多线程死锁的一个方法
问题:要保证生产者不会在缓存区满的时候加入数据,消费者也不会在缓存区空的时候消费数据。
通常方法有:信号灯法,管程法
package com.thread.create;
public class TestProduct_Consumer {
public static void main(String[] args) {
Movie movie = new Movie();
Play play = new Play(movie);
Watch watch = new Watch(movie);
new Thread(play).start();
new Thread(watch).start();
}
}
//共同的资源
class Movie{
private String pic;
//信号灯 flag=true,生产者生产,消费者等待;生产完成后通知消费者 。flag = false 生产者等待,消费者消费,消费完成后通知生产者
boolean flag = true;
public synchronized void play(String pic) throws InterruptedException {
if (!flag) { //生产者等待
this.wait();
}
//开始生产
Thread.sleep(500);
this.pic = pic;
System.out.println("生产了--》"+pic);
//生产完成,通知消费
this.notifyAll();
flag = false;
}
public synchronized void watch() throws InterruptedException {
if (flag) { //消费者等待
this.wait();
}
//开始消费
Thread.sleep(200);
System.out.println("消费了--》"+pic);
//通知生产
this.notifyAll();
flag = true;
}
}
//生产者
class Play implements Runnable{
private Movie movie;
@Override
public void run() {
for (int i = 0;i<100;i++) {
if (0==i%2) {
try {
movie.play("左青龙");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
try {
movie.play("右白虎");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public Play(Movie movie) {
super();
this.movie = movie;
}
}
//消费者
class Watch implements Runnable{
private Movie movie;
@Override
public void run() {
for (int i = 0;i<100;i++) {
try {
movie.watch();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public Watch(Movie movie) {
super();
this.movie = movie;
}
}
4.任务调度
Timer定时器类:实现定时功能(每隔一段时间触发一次线程)
TimerTask任务类:是一个抽象类,该类实现了runnable接口,因此有多线程的功能。
通过Timer,TimerTask(Spring的任务调度就是通过他们来实现的)
通过继承TimerTask获得多线程能力,将代码写入run()方法,通过Timer类启动多线程。
package com.thread.create;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TestTimerTask {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("so easy...");
}
}, new Date(System.currentTimeMillis()+1000),100);
}
}
5.线程池
线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
线程池的工作机制 : 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。 一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
使用线程池的原因: 多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。这时,线程池就是最好的选择了。
线程池实现的4种方式(返回值都是ExecutorService, ExecutorService是Java提供的用于管理线程池的类。该类的作用是控制线程数量和重用线程):
(1):Executors.newCacheThreadPool():可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务。
package com.thread.create;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) throws InterruptedException {
//创建一个可缓存线程
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i=0;i<10;i++) {
//sleep可明显看到使用的是线程池里面以前的线程,没有创建新的线程
Thread.sleep(1000);
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在被执行");
}
});
}
}
}
线程池为无限大,当执行当前任务时上一个任务已经完成,会复用执行上一个任务的线程,而不用每次新建线程。
(2) Executors.newFixedThreadPool(int n):创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。
package com.thread.create;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) throws InterruptedException {
//创建一个可重用固定个数的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i=0;i<10;i++) {
Thread.sleep(1000);
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在被执行");
}
});
}
}
}
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()
(3)Executors.newScheduledThreadPool(int n):创建一个定长线程池,支持定时及周期性任务执行。
package com.thread.create;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
public static void main(String[] args) throws InterruptedException {
//创建一个定长线程池,支持定时及周期性任务执行——延迟执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
for (int i=0;i<10;i++) {
//延迟1秒执行
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在被执行");
}
},1,TimeUnit.SECONDS);
}
}
}
定期执行示例代码:
package com.thread.create;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
public static void main(String[] args) throws InterruptedException {
//创建一个定长线程池,支持定时及周期性任务执行——延迟执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
for (int i=0;i<10;i++) {
//延迟1秒执行
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"在被执行");
}
},1,3,TimeUnit.SECONDS);
}
}
}
(4)Executors.newSingleThreadExecutor():创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
package com.thread.create;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) throws InterruptedException {
//创建一个单线程化的线程池
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
for (int i=0;i<10;i++) {
int index = i;
//延迟1秒执行
singleThreadPool.execute(new Runnable() {
@Override
public void run() {
//结果依次输出,相当于顺序执行各个任务
System.out.println(Thread.currentThread().getName()+"正在被执行,打印的值是:"+index);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
(5)自定义线程池
缓冲队列BlockingQueue简介: BlockingQueue是双缓冲队列。BlockingQueue内部使用两条队列,允许两个线程同时向队列一个存储,一个取出操作。在保证并发安全的同时,提高了队列的存取效率。
常用的几种BlockingQueue:
- ArrayBlockingQueue(int i):规定大小的BlockingQueue,其构造必须指定大小。其所含的对象是FIFO顺序排序的。
- LinkedBlockingQueue()或者(int i):大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是FIFO顺序排序的。
- PriorityBlockingQueue()或者(int i):类似于LinkedBlockingQueue,但是其所含对象的排序不是FIFO,而是依据对象的自然顺序或者构造函数的Comparator决定。
- SynchronizedQueue():特殊的BlockingQueue,对其的操作必须是放和取交替完成。
Executors提供四种线程池,一般都不用Executors提供的线程创建方式,使用ThreadPoolExecutor创建线程池
自定义线程池,可以用ThreadPoolExecutor类创建,它有多个构造方法来创建线程池。
常见的构造函数:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
package com.thread.create;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
public static void main(String[] args) throws InterruptedException {
// 创建数组型缓冲等待队列
BlockingQueue<Runnable> bq = new ArrayBlockingQueue<>(10);
// ThreadPoolExecutor:创建自定义线程池,池中保存的线程数为3,允许最大的线程数为6
ThreadPoolExecutor tpe = new ThreadPoolExecutor(3, 6, 50, TimeUnit.MILLISECONDS, bq);
// 创建3个任务
Runnable t1 = new TempThread();
Runnable t2 = new TempThread();
Runnable t3 = new TempThread();
tpe.execute(t1);
tpe.execute(t2);
tpe.execute(t3);
tpe.shutdown();
}
}
class TempThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在被执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
};
}
}
问题:
线程池使用的是哪种?
1.newCachedThreadPool创建一个可缓存线程池程
查看源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
通过ThreadPoolExecutor创建核心线程数为0,最大线程数为 Integer.MAX_VALUE的线程池,并设置keepAliveTime为60秒。
2.newFixedThreadPool 创建一个定长线程池
查看源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
通过ThreadPoolExecutor创建核心线程数为n,最大线程数为n的线程池,并设置BlockingQueue为LinkedBlockingQueue。
3.newScheduledThreadPool 创建一个定长线程池
查看源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
通过ScheduledThreadPoolExecutor创建线程池,查看ScheduledThreadPoolExecutor源码:
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService {
。。。。。。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public ScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), handler);
}
}
继承了ThreadPoolExecutor并实现了ScheduledExecutorService 。构造方法使用父类即ThreadPoolExecutor构造方法。
4.newSingleThreadExecutor 创建一个单线程化的线程池
查看源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
返回的是new FinalizableDelegatedExecutorService实例,继续看FinalizableDelegatedExecutorService源码:
static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}
发现1.返回的是父类DelegatedExecutorService的对象,DelegatedExecutorService实现AbstractExecutorService ,而ThreadPoolExecutor 也实现AbstractExecutorService ,因此newSingleThreadExecutor 创建的对象依然和ThreadPoolExecutor 创建的对象一致,相当于走了一个代理类而已。2.拥有finalize()方法,在GC的时候时触发未被使用的线程池关闭。
static class DelegatedExecutorService extends AbstractExecutorService {
。。。。。。
}
public class ThreadPoolExecutor extends AbstractExecutorService {
。。。。。。
}
5.ThreadPoolExecutor( //
自定义线程
int
corePoolSize,
//核心池的大小
int
maximumPoolSize,
//池中允许的最大线程数,这个参数表示了线程池中最多能创建的线程数量
long
keepAliveTime,
//当线程数大于corePoolSize时,终止前多余的空闲线程等待新任务的最长时间
TimeUnit unit,
//keepAliveTime时间单位
BlockingQueue<Runnable> workQueue,
//存储还没来得及执行的任务
ThreadFactory threadFactory,
//执行程序创建新线程时使用的工厂
RejectedExecutionHandler handler //超出线程范围和队列容量而使执行被阻塞时所使用的处理程序
)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
当不传ThreadFactory 时,会默认使用defaultThreadFactory。
构造的线程池前缀为pool-xxxxx-thread-1 ,如所示:
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
。。。。。。
}
自定义线程池参数有哪些?
1.corePoolSize:核心线程数
- 核心线程会一直存活,即使没有任务需要执行
- 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
- 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
2.BlockingQueue:阻塞队列
当核心线程数达到最大时,新任务会放在队列中排队等待执行。
3.maxPoolSize:最大线程数
- 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
- 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常(
RejectedExecutionHandler
)
4.keepAliveTime:线程空闲时间
- 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
- 如果allowCoreThreadTimeout=true,则会直到线程数量=0
5.rejectedExecutionHandler:任务拒绝处理器
- 两种情况会拒绝处理任务:
- 当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务
- 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
- 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
- ThreadPoolExecutor类有几个内部实现类来处理这类情况:
- AbortPolicy 丢弃任务,抛运行时异常
- CallerRunsPolicy 执行任务
- DiscardPolicy 忽视,什么都不会发生
- DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
- 实现RejectedExecutionHandler接口,可自定义处理器
线程池执行流程?
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满
- 若线程数小于最大线程数,创建线程
- 若线程数等于最大线程数,抛出异常,拒绝任务
自定义线程池参数怎么配置?
- 如何来设置
- 需要根据几个值来决定
- tasks :每秒的任务数,假设为500~1000
- taskcost:每个任务花费时间,假设为0.1s
- responsetime:系统允许容忍的最大响应时间,假设为1s
- 做几个计算
- corePoolSize = 每秒需要多少个线程处理?
- threadcount = tasks/(1/taskcost) =tasks*taskcout = (500~1000)*0.1 = 50~100 个线程。corePoolSize设置应该大于50
- 根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可
- queueCapacity = (coreSizePool/taskcost)*responsetime
- 计算可得 queueCapacity = 80/0.1*1 = 80。意思是队列里的线程可以等待1s,超过了的需要新开线程来执行
- 切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新开线程来执行,响应时间会随之陡增。
- maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)
- 计算可得 maxPoolSize = (1000-80)/10 = 92
- (最大任务数-队列容量)/每个线程每秒处理能力 = 最大线程数
- rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理
- keepAliveTime和allowCoreThreadTimeout采用默认通常能满足
- 以上都是理想值,实际情况下要根据机器性能来决定。如果在未达到最大线程数的情况机器cpu load已经满了,则需要通过升级硬件(呵呵)和优化代码,降低taskcost来处理。