Java高并发程序-Chapter3 JDK并发包(第十九讲)线程复用:线程池


1. 为什么使用线程池?

由于真实环境的需要,可能会开启很多线程来支撑其应用。而当线程数量过大时,反而会耗尽CPU和内存资源。

虽然与进程相比,线程是一种轻量级的工具,但其创建和关闭依然需要花费时间,如果为每一个小的任务都创建一个线程,很有可能出现创建和销毁线程所占用的时间大于该线程真实工作所消耗的时间的情况,反而会得不偿失

2. 什么是线程池

线程池中,总有那么几个活跃线程。当你需要使用线程时,可以从池子中随便拿一个空闲线程,当完成工作时,并不急着关闭线程,而是将这个线程退回到池子,方便其他人使用。



3. 自定义简单的线程池

package com.john.learn.high.concurent.ch03.threadpool.simple;

import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

public class SimpleThreadPool {

	public SimpleThreadPool() {
		workers = new ConcurrentLinkedQueue<>();
	}

	public void submit(Runnable runnable) {

		Worker worker = workers.poll();

		if (worker != null) {

			worker.setTarget(runnable);

			return;
		}

		worker = new Worker(this, runnable);

		worker.start();
		
		
	}

	public boolean returnPool(Worker worker) {

		if (worker.isStop() || !worker.isAlive()) {

			return false;
		}

		workers.add(worker);

		return true;
	}

	private void shutdown() {

		for (Iterator<Worker> it = workers.iterator(); it.hasNext();) {

			it.next().stopTask();
		}
	}

	private ConcurrentLinkedQueue<Worker> workers;
	
	private boolean shutdown;

	public static void main(String[] args) throws InterruptedException {

		final AtomicInteger index = new AtomicInteger();

		Runnable task = new Runnable() {

			@Override
			public void run() {

				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {

					e.printStackTrace();
				}

				System.out.println("Task " + index.incrementAndGet() + " completed.");

			}
		};

		SimpleThreadPool pool = new SimpleThreadPool();

		for (int i = 0; i < 10; i++) {
			pool.submit(task);
		}

		Thread.sleep(1000);
		for (int i = 0; i < 10; i++) {
			pool.submit(task);
		}
		Thread.sleep(1000);
		pool.shutdown();
	}

}

package com.john.learn.high.concurent.ch03.threadpool.simple;

public class Worker extends Thread {

	public Worker(SimpleThreadPool simpleThreadPool, Runnable task) {
		this.simpleThreadPool = simpleThreadPool;
		this.task = task;
		stop = false;
		this.setDaemon(true);
	}

	public synchronized void setTarget(Runnable task) {
		/**
		 * 通知当前线程,Run Task
		 */
		this.task = task;
		this.notify();
	}

	@Override
	public void run() {

		while (!stop) {

			try {

				if (task != null) {

					task.run();

					task = null;

					// return thread Pool
					if (!simpleThreadPool.returnPool(this)) {

						return;
					}
				}

				synchronized (this) {
					try {
						/**
						 * 等待下一个Task
						 */
						this.wait();

					} catch (InterruptedException e) {

						this.notify();
					}
				}

			} catch (Throwable e) {

				this.notify();
			}

		}
	}

	public boolean isStop() {
		return stop;
	}

	public synchronized void stopTask() {
		/**
		 * stop Task
		 */
		this.stop = true;

		this.notifyAll();
	}

	private volatile boolean stop;

	private Runnable task;

	private SimpleThreadPool simpleThreadPool;
}

4. JDK对线程池的支持



5. 线程池的使用 - 线程池的种类

1. newFixedThreadPool()方法

该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。

当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。

若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。

 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {

        return new ThreadPoolExecutor(nThreads, nThreads,

                                      0L, TimeUnit.MILLISECONDS,

                                      new LinkedBlockingQueue<Runnable>(),

                                      threadFactory);

    }


2. newSingleThreadExecutor()方法

该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。


 3. newCachedThreadPool()方法

该方法返回一个可根据实际情况调整线程数量的线程池。

线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。

若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。

所有线程在当前任务执行完毕后,将返回线程池进行复用。

public static ExecutorService newCachedThreadPool() {

        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                      60L, TimeUnit.SECONDS,

                                      new SynchronousQueue<Runnable>());

    }

    

4. newSingleThreadScheduledExecutor()方法

该方法返回一个 ScheduledExecutorService对象,线程池大小为1。

ScheduledExecutorService接口在 Executorservice接口之上扩展了在给定时间执行某任务的功能,

如在某个固定的延时之后执行,或者周期性执行某个任务。

5. newScheduledThreadPool()方法

该方法也返回一个 ScheduledExecutorService对象,但该线程池可以指定线程数量。



      

 

6. 不同线程池的共同性

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


    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

 CorePoolSize: 指定线程池的线程数量

 maximunPoolSize:指定线程池最大线程数量

keepAliveTime:当线程池线程超过CorePoolSize时,多余的线程存活时间

Timeunit:时间单位

BlockingQueue:任务队列,保存未处理Task

7. 扩展和新增线程池

a. 回调接口

 private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
 final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
class PausableThreadPoolExecutor extends ThreadPoolExecutor {
 *   private boolean isPaused;
 *   private ReentrantLock pauseLock = new ReentrantLock();
 *   private Condition unpaused = pauseLock.newCondition();
 *
 *   public PausableThreadPoolExecutor(...) { super(...); }
 *
 *   protected void beforeExecute(Thread t, Runnable r) {
 *     super.beforeExecute(t, r);
 *     pauseLock.lock();
 *     try {
 *       while (isPaused) unpaused.await();
 *     } catch (InterruptedException ie) {
 *       t.interrupt();
 *     } finally {
 *       pauseLock.unlock();
 *     }
 *   }
 *
 *   public void pause() {
 *     pauseLock.lock();
 *     try {
 *       isPaused = true;
 *     } finally {
 *       pauseLock.unlock();
 *     }
 *   }
 *
 *   public void resume() {
 *     pauseLock.lock();
 *     try {
 *       isPaused = false;
 *       unpaused.signalAll();
 *     } finally {
 *       pauseLock.unlock();
 *     }
 *   }
 * }}</pre>

b. 拒绝策略

ThreadPoolExecutor的最后一个参数指定了拒绝策略。
也就是当任务数量超过系统实际承载能力时,该如何处理呢?这时就要用到拒绝策略了。
拒绝策略可以说是系统超负荷运行时的补救措施,通常由于压力太大而引起的,也就是线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列中也已经排满了,再也塞不下新任务了

 JDK内置的拒绝策略如下
 AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作
 CallerRunsPolicy策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降
 DiscardOledestPolicy策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务
 DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,我觉得这可能是最好的一种方案了吧!
以上内置的策略均实现了 RejectedExecutionHandler接口,若以上策略仍无法满足实际应用需要,完全可以自己扩展 RejectedExecutionHandler接口

c. 自定义 ThreadFactory

ThreadFactory是一个接口,它只有一个方法,用来创建线程:
Thread newThread(Runnable r)
当线程池需要新建线程时,就会调用这个方法自定义线程池可以帮助我们做不少事。
比如,我们可以跟踪线程池究竟在何时创建了多少线程,也可以自定义线程的名称、组以及优先级等信息,甚至可以任性地将所有的线程设置为守护线程。
总之,使用自定义线程池可以让我们更加自由地设置池子中所有线程的状态。

下面的案例使用自定义的 ThreadFactory,一方面记录了线程的创建,另一方面将所有的线程都设置为守护线程,这样,当主线程退出后,将会强制销毁线程池。


8. 线程池核心调度代码




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值