一、概念

1、并行(parallellism)和并发(concurrency)

QQ和微信同时运行,是并行。在微信上和多个人聊天,是并发。

并行是指同一时刻同时做多件事情,并发是指同一时间间隔内做多件事情。

并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

 

2、进程(process)和线程(thread)

线程状态:

java多线程重复处理 java多线程并行执行方法_java 多线程交替执行

线程方法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接口,可自定义处理器

线程池执行流程?

  1. 当线程数小于核心线程数时,创建线程。
  2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  3. 当线程数大于等于核心线程数,且任务队列已满
  1. 若线程数小于最大线程数,创建线程
  2. 若线程数等于最大线程数,抛出异常,拒绝任务

自定义线程池参数怎么配置?

  • 如何来设置
  • 需要根据几个值来决定
  • 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来处理。