java 四种线程池详细笔记整理

自带的四种线程池:

     Java通过Executors提供四种线程池,分别为

  1. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
  2. newFixedThreadPool  创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  3. newScheduledThreadPool 创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行。 
  4. newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 

自定义线程池ThreadPoolExecutor 

四种线程池本质都是创建ThreadPoolExecutor类,ThreadPoolExecutor构造参数如下

  1. int corePoolSize, 核心线程大小

  2. int maximumPoolSize,最大线程池大小,最大值Integer.MAX_VALUE

  3. long keepAliveTime, 超过corePoolSize的线程多久不活动被销毁时间

  4. TimeUnit unit,时间单位

  5. BlockingQueue<Runnable> workQueue 任务队列

  6. ThreadFactory threadFactory 线程池工厂

  7. RejectedExecutionHandler handler 拒绝策略

任务队列

  1. ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列

  2. LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列(常用)

  3. PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列

  4. DelayQueue: 一个使用优先级队列实现的无界阻塞队列

  5. SynchronousQueue: 一个不存储元素的阻塞队列(常用)

  6. LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列

  7. LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列

各个线程池的参数对比

线程池和默认参数
线程池corePoolSizemaximumPoolSizekeepAliveTimeunitworkQueue
newSingleThreadExecutor 110
LinkedBlockingQueue
newFixedThreadPool必填跟corePoolSize一致0
LinkedBlockingQueue
newScheduledThreadPool 必填Integer.MAX_VALUE0
DelayedWorkQueue
newCachedThreadPool 0

Integer.MAX_VALUE

60
TimeUnit.SECONDS
SynchronousQueue

 

 

 

 

 

 

 

 

 

 

 

 

代码测试案例:

 

公共代码,线程类:

package thread;

import java.util.Date;

// 线程测试类
public class MyRunnable implements Runnable{
	private int i ;
	public MyRunnable(int i){
		this.i = i;
	}
	@Override
	public void run() {
        System.out.println(" 第"+i+"次-线程名称:" + Thread.currentThread().getName() +"  时间:"+ new Date());
        try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}

1.newSingleThreadExecutor 

测试代码:

package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 单线程线程池 - 只有一个线程执行程序,执行完一次再执行一次
 */
public class TestSingleThreadExecutor {
	public static void main(String[] args) {
		        ExecutorService  threadPool =  Executors.newSingleThreadExecutor();
		        for(int i=0;i<10 ;i++){
		        	MyRunnable r = new  MyRunnable(i);
		        	threadPool.execute(r);
		        }
	}
}

运行结果:

:

总结:都是一个线程在执行

2.newFixedThreadPool  

测试代码:

package thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 定长线程池
 */
public class TestFixedThreadPool {
	public static void main(String[] args) {
		        /**
		         * 固定长度的线程池,循环使用线程,线程下标1~3 然后再到1~3,循环往复
		         */
		        ExecutorService  threadPool =  Executors.newFixedThreadPool(3);
		        for(int i=0;i<10 ;i++){
		        	MyRunnable r = new  MyRunnable(i);
		        	threadPool.execute(r);
		        }
	}
}

运行结果:

总结:定长线程池,循环使用线程,没有空闲的则需要等待,运行完空闲了也不会销毁线程

3.newScheduledThreadPool 

测试代码:

package thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 可定期或者延期执行的线程池
 */
public class TestScheduledThreadPool {
	public static void main(String[] args) {
        // 需要用 ScheduledExecutorService 接口接收,好调用内部的方法
        	ScheduledExecutorService threadPool =  Executors.newScheduledThreadPool(3);
		     // 普通任务执行
		        for(int i=0; i<10 ; i++){
		        	MyRunnable r = new  MyRunnable(i);
		            threadPool.execute(r);
		            try {
		                Thread.sleep(1000);//睡眠1s
		            } catch (InterruptedException e) {
		                e.printStackTrace();
		            }
		        }
	        	MyRunnable r = new  MyRunnable(100);

		        System.out.println("定时线程begin");
		        // 3秒后再执行任务。第一参数是任务,第二个是时间数值,第三个是指定时间单位的枚举,这里指定是秒
		        threadPool.schedule(r,3, TimeUnit.SECONDS);//
		        System.out.println("定时线程end");
	        	MyRunnable r2 = new  MyRunnable(200);

		        System.out.println("循环线程begin");
		        // 5秒后再执行任务,然后每3秒执行一次。第一参数是任务,第二个是延迟时间值,第三个是每隔多久执行一次时间值,第四个是指定时间单位的枚举,这里指定是秒
		        threadPool.scheduleAtFixedRate(r2,5,3,TimeUnit.SECONDS);
		        System.out.println("循环线程end");
	}
}

运行结果:

总结:可以指定时间和时间周期运行,创建需要传入corePoolSize参数,如果线程没用空闲还是会等待有空闲再调用。试着传入corePoolSize为0,只有一个线程处理任务

4.newCachedThreadPool 

测试代码:

package thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
   * 缓存线程池
 */
public class TestCachedThreadPool {
	public static void main(String[] args) {
        	ExecutorService  threadPool =  Executors.newCachedThreadPool();
		        for(int i=0;i<10 ;i++){
		        	MyRunnable r = new  MyRunnable(i);
		        	threadPool.execute(r);
		        }
	}
}

运行结果:

总结:默认corePoolSize为0,

5.自定义线程池ThreadPoolExecutor 

测试代码:

package thread;

import java.util.Date;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

// 自定义连接池 
public class TestThreadThreadPoolExecutor {

	public static void main(String[] args) {
		BlockingQueue<Runnable> queue =  new LinkedBlockingQueue (1);
		/**
		 * 如果队列容量为 1 ,线程池最大容量maximumPoolSize为3,要执行的线程超过4(1+3=4)。
		 *   queue + maximumPoolSize
		 */
		Executor  executor = new ThreadPoolExecutor(2, 3, 60, TimeUnit.SECONDS, queue);
        for(int i=0; i<20; i++){
        	MyRunnable r = new  MyRunnable(i);
        	executor.execute(r);
        }
		
        
	}
}

运行结果:

总结:为什么会报异常呢,因为要执行的线程为20,大于queue + maximumPoolSize  20>1+3。 所以会报异常。把线程池maximumPoolSize或者queue改大一些就可以了。这个总数一般要预估的,一般是两倍足够了。上面4种都有缺陷,听说阿里这些大厂不用原生的,要自己写。原生的看都有这一些缺陷,我们自己写细节还是要注意一下了

maximumPoolSize  改为20,执行没报错,结果如下。

备注:

  • 一般如果线程池任务队列采用LinkedBlockingQueue队列的话,那么不会拒绝任何任务(因为其大小为Integer.MAX_VALUE),这种情况下,ThreadPoolExecutor最多仅会按照最小线程数corePoolSize来创建线程,也就是说线程池大小被忽略了。
  • 如果线程池任务队列采用ArrayBlockingQueue队列,初始化设置了最大队列数。那么ThreadPoolExecutor的maximumPoolSize才会生效,那么ThreadPoolExecutor的maximumPoolSize才会生效会采用新的算法处理任务,
  • 例如假定线程池的最小线程数为4,最大为8,ArrayBlockingQueue最大为10。随着任务到达并被放到队列中,线程池中最多运行4个线程(即核心线程数)直到队列完全填满,也就是说等待状态的任务小于等于10,ThreadPoolExecutor也只会利用4个核心线程线程处理任务。
  • 如果队列已满,而又有新任务进来,此时才会启动一个新线程,这里不会因为队列已满而拒接该任务,相反会启动一个新线程。新线程会运行队列中的第一个任务,为新来的任务腾出空间。如果线程数已经等于最大线程数,任务队列也已经满了,则线程池会拒绝这个任务,默认拒绝策略是抛出异常。
  • 这个算法的理念是:该池大部分时间仅使用核心线程(4个),即使有适量的任务在队列中等待运行。这时线程池就可以用作节流阀。如果挤压的请求变得非常多,这时该池就会尝试运行更多的线程来清理;这时第二个节流阀—最大线程数就起作用了。
  • CPU密集型和IO密集型: CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%;IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。创建线程池需要考虑到这两个服务器条件,如果cpu密集型,需要考虑是否需要少量核心进程,队列大一些;Io密集型是否考虑核心进程数设置大一些,减少队列大小等等

参考网站:

https://www.cnblogs.com/zincredible/p/10984459.html

https://blog.csdn.net/youanyyou/article/details/78990156

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值