JUC并发编程(11)——线程池

一、自定义线程池

任务队列

class BlockingQueue<T> {
	// 任务队列
	private Deque<T> queue = new ArrayDeque<>();
	// 锁
	private ReentrantLock lock = new ReentrantLock();
	// 生产者条件变量
	private Condition fullWaitSet = lock.newCondition(); 
	// 消费者条件变量
	private Condition emptyWaitSet = lock.newCondition(); 
	// 容量
	private int capcity;

	// 阻塞获取
	public void take(T element) {
		lock.lock();
		try {
			while(queue.isEmpty()) {
				try {
					emptyWaitSet .await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			T t = queue.removeFirst();
			return t;
		} finally {
			lock.unlock();
		}
	}

	// 带超时的阻塞获取
	public T poll(Long timeout, TimeUnit unit) {
		lock.lock();
		try {
			// 将timeout转化为纳秒
			long nanos = unit.toNanos(timeout);
			while(queue.isEmpty()) {
				try {
					if(nanos <= 0) {
						return null;
					}
					// 返回剩余时间
					nanos = emptyWaitSet.await(nanos);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			T t = queue.removeFirst();
			return t;
		} finally {
			lock.unlock();
		}
	}
	
	// 阻塞添加
	public void put(T element) {
		lock.lock();
		try {
			while(queue.size() == capcity) {
				try {
					fullWaitSet.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}	
			}
			queue.addLast(element);
			emptyWaitSet.signal();
		} finally {
			lock.unlock();
		}
	}
	
	// 带超时时间阻塞添加
	public boolean offer(T task, long timeout, TimeUnit timeUnit) {
		lock.lock();
		try {
			long nanos = timeUnit.toNanos(timeout);
			while(queue.size() == capcity) {
				try {
					if(nanos <= 0) {
						return false;
					}
					nanos = fullWaitSet.awaitNaos(nanos );
				} catch (InterruptedException e) {
					e.printStackTrace();
				}	
			}
			queue.addLast(element);
			emptyWaitSet.signal();
		} finally {
			lock.unlock();
		}
	}
	// 获取大小
	public int size() {
		lock.lock();
		try {
			return queue.size();
		} finally {
			lock.unlock();
		}
	}

	// 带拒绝策略的put
	public void tryPut(RejectPolicy<T> rejectPolicy, T task) {
		lock.lock();
		try {
			if(queue.size() == capcity) {
				rejectPolicy.reject(this,task);
			} else {
				queue.addLast(task);
				emptyWaitSet.signal();
			}
		} finally {
			lock.unlock();
		}
	}
}

线程池

class ThreadPool {
	// 任务队列,需要执行的任务队列
	private BlockingQueue<Runnable> taskQueue;
	// 线程集合
	private HashSet<Worker> workers = new HashSet<>();
	// 核心线程数
	private int coreSize;
	// 超时时间
	private long timeout;
	// 单位
	private TimeUnit timeUnit;
	private RejectPolicy<Runnable> rejectPolicy;

	public ThreadPool(int coreSize,long timeout,TimeUnit timeUnit,int queueCapcity,  RejectPolicy<Runnable> rejectPolicy) {
		this.coreSize = coreSize;
		this.timeout = timeout;
		this.timeUnit = timeUnit;
		this.taskQueue = new BlockingQueue<>(queueCapcity);
		this.rejectPolicy = rejectPolicy;
	}
	
	class Worker extends Thread{
		private Runnable task;
		
		public Worker(Runnable task) {
			this.task = task;
		}
		@Override
		public void run() {
			while(task != null || (task = taskQueue.take()) != null){
				try {
					task.run();
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					task = null;
				}
			}
			synchronized(workers){
				workers.remove(this);
			}
		}
	}
	
	public void execute(Runnable task) {
		synchronized(workers){
			if (workers.size() < coreSize){
				Worker worker = new Worker(task);
				workers.add(worker)
				worker.start();
			} else {
				// 没使用拒绝策略的put,就是一直死等 
				// taskQueue.put(task);
				// 带有自定义拒绝策略的put,队列满时根据拒绝策略进行处理
				taskQueue.tryPut(rejectPolicy,task);
			}
		}
	}
}

拒绝策略

@FunctionalInterface // 拒绝策略
interface RejectPolicy<T> {
	void reject(BlockingQueue<T> queue,T task);
}

二、JDK线程池

请添加图片描述

1. 线程池状态

ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位表示线程数量。

状态名高3位置接收新任务处理阻塞队列任务说明
RUNNING111YY
SHUTDOWN000NY不会接收新任务,但是会处理阻塞队列剩余任务
STOP111NN会中断正在执行的任务,并抛弃阻塞队列任务
TIDYING111任务全执行完毕,活动线程为0即将进入终结
TERMINATED111终结状态
// 将线程池状态和线程个数合二为一,就可以通过一次cas原子操作来辅助
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

2. 构造方法

public ThreadPoolExecutor(int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue,
            ThreadFactory threadFactory,
            RejectedExecutionHandler handler) 

① corePoolSize 核心线程数(最多保留的线程数)
② maximumPoolSize 最大线程数
③ keepAliveTime 生存时间
④ unit 时间单位
⑤ workQueue 阻塞队列
⑥ threadFactory 线程工程-可以为线程创建时起名
⑦ handler 拒绝策略

3. 工作原理

① 线程池中的线程都是懒加载,也就是刚开始线程池中并没有工作线程。
② 当有一个任务提交时,首先判断工作线程数是否小于核心线程数,如果少于则创造新的工作线程去处理任务。
③ 当工作线程数超过核心线程数时,就会加入到阻塞队列中。
④ 当阻塞队列满时,添加到阻塞队列失败,当工作线程少于最大线程数时就会创造救急线程处理任务。
⑤ 当工作线程总数等于最大线程数,阻塞队列也满时,则会进行拒绝策略。
⑥ 当线程空闲时间大于生存时间,救急线程会关闭,核心线程不会关闭。

4. 拒绝策略

如果线程到达最大线程数仍然有新任务,此时就会执行拒绝策略,jdk提供了4种拒绝策略的实现。
请添加图片描述
① AbortPolicy抛出RejectedExecutionException异常,这是默认策略
② CallerRunsPolicy 让调用者执行任务
③ DiscardPolicy 放弃本次任务
④ DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之

5. Executors类

该类提供了很多工厂方法来创建各种用途的线程池
newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
   return new ThreadPoolExecutor(nThreads,
   								nThreads,
                                     0L, 
                               TimeUnit.MILLISECONDS,
               new LinkedBlockingQueue<Runnable>());
    }

固定大小的线程池,核心线程数等于最大线程数,阻塞队列是无界的,适用于任务量已知场景。

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0,
        							Integer.MAX_VALUE,
                                      60L,
                                      TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>());
    }

核心线程数时0,最大线程数是最大整数。救急线程空闲时间为60s。队列采用了SynchronousQueue,它没有容量,放置认为的线程会被阻塞住直到工作线程取到。

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

线程数固定大小为1,单线程执行。如果自己创建一个单线程执行任务,如果任务失败会导致这个线程终止,而该线程池会创建一个新的线程,保证后续正常工作。

6. 线程池相关方法

① void execute(Runnable command); 执行任务。
② <T> Future submit(Callable<T> task);
提交任务task,用返回值Future获得任务执行结果。
请添加图片描述

③ public List<Future> invokeAll(Collection<? extends Callable> tasks) ;
提交tasks中所有任务
请添加图片描述

④ public T invokeAny(Collection<? extends Callable> tasks) ;
提交tasks中所有任务,那个任务先成功执行完成,返回此任务执行结果,其他任务取消。
请添加图片描述
⑤ void shutdown();
不会接收新任务,但是已提交的任务会执行完。不会阻塞调用线程的执行

7. Timer类

在任务调度线程池功能加入之前,可以使用java.util.Timer来实现定时功能。Timer的优点在于简单易用,但由于所有任务都是由同一线程来调度,所以任务都是串行执行。
例如下图中任务1和任务2都是延迟一秒执行,但是如果任务一耗时较长,也会导致任务二必须等待任务一结束才能执行。
请添加图片描述

7. Executors的任务调度线程池

当任务数少于线程数时,是并行执行,互不干扰。如果任务数大于线程数,还是会产生Timer类的问题。

ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);

// 延迟执行
pool.schedule(() -> {
	System.out.println("task1");
	sleep(2);
},1,TimeUnit.SECONDS);

pool.schedule(() -> {
	System.out.println("task2");
	sleep(2);
},1,TimeUnit.SECONDS);

// 定时执行,延迟2s后执行,之后每1s执行一次。但是如果当执行任务时间大于定时时间,则按照任务执行完时间定时执行。
pool.scheduleAtFixedRate(() -> {
	System.out.println("task3");
	sleep(2);
},2,1,TimeUnit.SECONDS);

pool.scheduleAtFixedRate(() -> {
	System.out.println("task4");
	sleep(2);
},2,1,TimeUnit.SECONDS);

// 延迟2s后定时执行任务,定时时间从上一个任务结束时开始计时。例如上一个任务要3s执行完,则4s后才会执行该任务。
pool.scheduleWithFixedDelay(() -> {
	System.out.println("task4");
	sleep(2);
},2,1,TimeUnit.SECONDS);

例子:每周四 18:00:00定时执行任务

请添加图片描述

8.线程池处理异常

线程池中的任务如果执行过程中产生异常,也不会抛出异常,所以处理方法如下:
① 自己主动try catch
② 使用带返回值的执行方法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值