javade多任务处理之Executors框架(线程池)实现的内置几种方式与两种基本自定义方式

一 Executors框架(线程池)

主要是解决开发人员进行线程的有效控制,原理可以看jdk源码,主要是由java.uitl.concurrent.ThreadPoolExecutor类实现的,这里只列出简单用法

根据Executors可以创建不同功能的线程池,主要有四种:

1 newFixedThreadPool : 返回一个固定数量的线程池,并且池中数量一致保持不变,有任务时如果有空闲线程则立即执行,没有就暂时存放到队列等待空闲线程

//创建一个有10个线程的线程池,任务多于10个会一直等待,直到10个线程中有空闲线程为止
//ExecutorService pool = Executors.newFixedThreadPool(10);
//开启线程
pool.execute(new Thread(()->{System.out.println(Thread.currentThread().getName()+"执行中.....");}));
pool.execute(new Thread(()->{System.out.println(Thread.currentThread().getName()+"执行中.....");}));
pool.execute(new Thread(()->{System.out.println(Thread.currentThread().getName()+"执行中.....");}));

 在ThreadPoolExecutor类中有几个非常重要的方法:

  execute()   执行线程

  submit()  执行线程,并返回执行的结果

  shutdown()  关闭线程,如果有线程正在执行,则会等线程执行完,即所有线程空闲之后再关闭

  shutdownNow()  关闭线程,立刻关闭

看源码可知,底层任务使用了阻塞队列,任务大于线程数量后会进入阻塞队列等待执行

2 newSingleThreadExecutor

		ExecutorService poolSingle = Executors.newSingleThreadExecutor();
		poolSingle.execute(new Thread(()->{
			System.out.println("run1");
			while(true){
			}
		}));
		//下面永不会执行
		poolSingle.execute(new Thread(()->{System.out.println("run2");}));

  创建只有一个线程的线程池,属于上面固定数量的一个特例,个人感觉这个单例线程用处不是很大

3 newCachedThreadPool

		不指定数量.如果任务多,且没有空闲线程则会一直创建线程,并且空闲线程会在60秒后回收
		ExecutorService cachePool = Executors.newCachedThreadPool();
		cachePool.execute(new Thread(()->{
			System.out.println("run1");
			while(true){
			}
		}));
		cachePool.execute(new Thread(()->{
			System.out.println("run2");
			while(true){
			}
		}));

  底层实现:

由于使用了无缓冲队列,任务队列使用无缓冲队列,任务不会被存储,直接去交给线程执行.且空闲线程60秒之后会被回收,除非60秒之内去执行新的任务

4 newScheduledThreadPool

class RunTest implements Runnable{
	@Override
	public void run() {
		System.out.println("run");
	}
}
public class ScheculedTest {
	public static void main(String[] args) {
		ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
		//可实现任务定时,但实际业务中一般都用spring的定时器,这个技术比较老了
		//pool.schedule(new RunTest(), 2, TimeUnit.SECONDS);//2秒后执行此任务
		//可实现任务的轮询,指定时间间隔循环执行此任务     任务    延迟时间(即2秒后开始执行)   间隔多少秒之后再执行(一直循环)
		pool.scheduleAtFixedRate(new RunTest(), 2, 2, TimeUnit.SECONDS);
		pool.scheduleWithFixedDelay(new RunTest(), 2, 2, TimeUnit.SECONDS);
	}
}

  不用想也知道,底层采用延迟队列装任务

二 自定义线程池实现的

再来看自定义的,实际业务中可能用到更多的就是自定义了,

自定义需要实现ThreadPoolExecutor这个类,主要有两种,分别是使用有界队列装任务和无界队列:

1 使用有界队列:

public class MyCustom01 {

	public static void main(String[] args) {
		//有界队列自定义线程池
		/**在使用有界队列时,如果有任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,
		 * 若大于corePoolSize,则会将任务加入队列,
		 * 若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,
		 * 若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。
		 */
		ThreadPoolExecutor pool = new ThreadPoolExecutor(
				1,   //核心线程数
				2,   //最大线程数
				60,   //空闲时回收时间
				TimeUnit.SECONDS,
				new ArrayBlockingQueue<Runnable>(3),//指定队列//指定队列
				//DiscardOldestPolicy :丢弃最老的请求,即当6进来时,将队列中最早的2丢弃,执行6
				//new ThreadPoolExecutor.DiscardOldestPolicy()//指定拒绝策略.不指定默认抛异常
				//自定义拒绝策略
				new RejectedExecutionHandler(){
					@Override        //r:被拒绝任务     executor 当前线程池
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						//实例工作中默认策略都不可取,一般是把拒绝的任务记录日志,然后空闲时间解析再重新执行
						//或者直接返回客户端,让他稍后再提交此任务
						System.out.println("自定义处理..");
						System.out.println("当前被拒绝任务为:" + r.toString());
						System.out.println(executor.getPoolSize());
					}}
		);
		MyTask mt1 = new MyTask(1, "任务1");
		MyTask mt2 = new MyTask(2, "任务2");
		MyTask mt3 = new MyTask(3, "任务3");
		MyTask mt4 = new MyTask(4, "任务4");
		MyTask mt5 = new MyTask(5, "任务5");
		MyTask mt6 = new MyTask(6, "任务6");
		pool.execute(mt1);
		pool.execute(mt2);//如果有两个任务,则会将第二个任务入队,等待核心线程线程空闲
		pool.execute(mt3);//同上
		pool.execute(mt4);//同上,知道队列满 为止
		pool.execute(mt5);//当有5个任务时,队列已满,则会创建第二个线程
		pool.execute(mt6); //有6个时,此时队列已满,且当前线程达到最大线程数,所以无法执行,执行拒绝策略
		//关闭,等到所有线程空闲时才会关闭
		pool.shutdown();
	}
}

  

2 使用无界队列:

public class MyCustom02 implements Runnable{
	//并发数字运算
	private static AtomicInteger count = new AtomicInteger(0);
	@Override
	public void run() {
		try {
			int temp = count.incrementAndGet();//count累加,区分任务
			System.out.println("任务" + temp);
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) throws Exception{
		//无界队列
		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
		ExecutorService executor  = new ThreadPoolExecutor(
					5,
					10,//无界时,任务多会一直添加到队列中,不会创建新的线程,线程数会一直是核心数量
					120L,
					TimeUnit.SECONDS,
					queue //指定无界队列
		);
		for(int i = 0 ; i < 20; i++){
			executor.execute(new MyCustom02()); //会5个一起执行
		}
		Thread.sleep(1000);
		System.out.println("queue size:" + queue.size());
		Thread.sleep(2000);
	}
}

  然后拒绝策略个人认为也是比较常用的,实际业务中,因为默认的策略都不太友好

原文地址:https://www.cnblogs.com/houzheng/p/9193031.html

时间: 2024-10-07 08:46:08

javade多任务处理之Executors框架(线程池)实现的内置几种方式与两种基本自定义方式的相关文章

戏(细)说Executor框架线程池任务执行全过程(上)

一.前言 1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦.要执行任务的人只需把Task描述清楚,然后提交即可.这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了.具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了. 经过这样的封装,对于使用者来说,提交任务获取结果的过程大大简化,调用者直接从提交

戏(细)说Executor框架线程池任务执行全过程(下)

上一篇文章中通过引入的一个例子介绍了在Executor框架下,提交一个任务的过程,这个过程就像我们老大的老大要找个老大来执行一个任务那样简单.并通过剖析ExecutorService的一种经典实现ThreadPoolExecutor来分析接收任务的主要逻辑,发现ThreadPoolExecutor的工作思路和我们带项目的老大的工作思路完全一致.在本文中我们将继续后面的步骤,着重描述下任务执行的过程和任务执行结果获取的过程.会很容易发现,这个过程我们更加熟悉,因为正是每天我们工作的过程.除了Thr

java多线程之Executor框架线程池详细介绍与ThreadPoolExecutor

Executor框架简介 Executor框架的结构 Executor框架主要由3大部分组成: 任务: 包括被执行的任务需要实现的接口:Runable 接口.Callable接口: 任务的执行: 包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口.Executor框架有两个关键类实现了ExecutorService接口:ThreadPoolExecutor 和 ScheduledThreadPoolExecutor.ForkJoinPool

Java多线程——Executors和线程池

线程池的概念与Executors类的应用 1.创建固定大小的线程池 package java_thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ThreadPoolTest { /** * @param args */ public static void mai

Executors 构建线程池

Executors包含一系列静态方法,可以用于构建线程池. 返回实现了 ExecutorService 接口的对象: newCachedThreadPool newFixedThreadPool(int threads) newSingleThreadPool 返回实现了ScheduledExecutorService接口的对象 newScheduledThreadPool(int threads) newScheduledSingleThreadPool 当用完一个线程池时,要调用shutdo

Java 并发:Executors 和线程池

让我们开始来从入门了解一下 Java 的并发编程. 本文主要介绍如何开始创建线程以及管理线程池,在 Java 语言中,一个最简单的线程如下代码所示: Runnable runnable = new Runnable(){ public void run(){ System.out.println("Run"); } } 可通过下面一行代码来启动这个线程: new Thread(runnable).start(); 这是一个再简单不过的例子了,但如果你有许多需要长时间运行的任务同时执行,

Java第三方数据库连接池库-DBCP-C3P0-Tomcat内置连接池

连接池原理 数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”.预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去.我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接. 现在流行的第三方Java数据库连接池库 DBCP 它是Apache推出的Database Connection Pool,属于Apache Commons开源项目,官网:http://commons.apache.org/components.html.Co

java——多线程的实现方式、两种办法解决线程赛跑

多线程的实现方式:demo1.demo2 demo1:继承Thread类,重写run()方法 package thread_test; public class ThreadDemo1 extends Thread { ThreadDemo1(){ } ThreadDemo1(String szName){ super(szName); } //重载run函数 public void run() { for(int count = 1 , row = 1 ; row < 10 ; row ++

【转】Java 并发:Executors 和线程池

原文地址: http://baptiste-wicht.com/posts/2010/09/java-concurrency-part-7-executors-and-thread-pools.html Java Concurrency - Part 7 : Executors and thread pools Baptiste Wicht 2010-09-15 07:17 38 Comments Source Let's start with a new post in the Java co