From 1dbf49911c4a2eb547fe79385913128c23a7484e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E9=A1=BA=E5=B9=B3?= <1078094401@qq.com> Date: Mon, 13 Jan 2020 20:51:37 +0800 Subject: [PATCH] =?UTF-8?q?059=5Fweek05=5F=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week_05/59/Executors.md | 104 +++++++++++ week_05/59/Thread.md | 309 +++++++++++++++++++++++++++++++ week_05/59/ThreadLocal.md | 146 +++++++++++++++ week_05/59/ThreadPoolExecutor.md | 206 +++++++++++++++++++++ 4 files changed, 765 insertions(+) create mode 100644 week_05/59/Executors.md create mode 100644 week_05/59/Thread.md create mode 100644 week_05/59/ThreadLocal.md create mode 100644 week_05/59/ThreadPoolExecutor.md diff --git a/week_05/59/Executors.md b/week_05/59/Executors.md new file mode 100644 index 0000000..6d17ec3 --- /dev/null +++ b/week_05/59/Executors.md @@ -0,0 +1,104 @@ +### Executors + +#### 简介 + +Executors是线程工具类,它定义了一个执行无返回的方法和一系列新建线程池的方法。 + +#### 源码分析 + +1.一系列新建线程池的方法 + +```java +public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + } + + public static ExecutorService newWorkStealingPool(int parallelism) { + return new ForkJoinPool + (parallelism, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); + } + public static ExecutorService newWorkStealingPool() { + return new ForkJoinPool + (Runtime.getRuntime().availableProcessors(), + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); + } + public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(), + threadFactory); + } + + public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); + } + + public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(), + threadFactory)); + } + + public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); + } + public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue(), + threadFactory); + } + + public static ScheduledExecutorService newSingleThreadScheduledExecutor() { + return new DelegatedScheduledExecutorService + (new ScheduledThreadPoolExecutor(1)); + } + + public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) { + return new DelegatedScheduledExecutorService + (new ScheduledThreadPoolExecutor(1, threadFactory)); + } + + public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { + return new ScheduledThreadPoolExecutor(corePoolSize); + } + + public static ScheduledExecutorService newScheduledThreadPool( + int corePoolSize, ThreadFactory threadFactory) { + return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); + } +``` + +2.线程运行回调Callable + +```java +public static Callable callable(final PrivilegedAction action) { + if (action == null) + throw new NullPointerException(); + return new Callable() { + public Object call() { return action.run(); }}; + } +``` + +#### 总结 + +​ FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。 +​ CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。 +​ 再结合注释看Executors类中的传参就明白了! +Executors中创建线程池: + +​ FixedThreadPool 和 SingleThreadExecutor 传入的最后一个参数阻塞队列 ”workQueue“,默认的长度是INTEGER.MAX_VALUE,而它们允许的最大线程数量又是有限的,所以当请求线程的任务过多线程不够用时,它们会在队列中等待,又因为队列的长度特别长,所以可能会堆积大量的请求,导致OOM。 + +​ CachedThreadPool 和 ScheduledThreadPool 它们的阻塞队列长度有限,但是传入的第二个参数maximumPoolSize 为Integer.MAX_VALUE,这就意味着当请求线程的任务过多线程不够而且队列也满了的时候,线程池就会创建新的线程,因为它允许的最大线程数量是相当大的,所以可能会创建大量线程,导致OOM。 \ No newline at end of file diff --git a/week_05/59/Thread.md b/week_05/59/Thread.md new file mode 100644 index 0000000..0399d09 --- /dev/null +++ b/week_05/59/Thread.md @@ -0,0 +1,309 @@ +### Thread类 + +本文参考自博客:https://www.cnblogs.com/albertrui/p/8391447.html + +#### 简介 + +​ 在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类中的方法的理解。 + +  线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。 + +  当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,譬如程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。 + +  当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。 + +  线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。 + +  当由于突然中断或者子任务执行完毕,线程就会被消亡。 + +#### Thread类源码分析 + +1.Thread实现了Runnable接口,Runnable中只有一个run方法需要实现。 + +```java +public +class Thread implements Runnable {} + +//Runnable +@FunctionalInterface +public interface Runnable { + /** + * When an object implementing interface Runnable is used + * to create a thread, starting the thread causes the object's + * run method to be called in that separately executing + * thread. + *

+ * The general contract of the method run is that it may + * take any action whatsoever. + * + * @see java.lang.Thread#run() + */ + public abstract void run(); +} +``` + +2.主要属性 + +```java +//线程名字 +private volatile String name; + //线程优先级 + private int priority; + private Thread threadQ; + private long eetop; + + /* Whether or not to single_step this thread. */ + private boolean single_step; + + /* Whether or not the thread is a daemon thread. */ +//是否是守护线程 + private boolean daemon = false; + + /* JVM state */ + private boolean stillborn = false; + //将要运行的任务 + /* What will be run. */ + private Runnable target; + + /* The group of this thread */ + private ThreadGroup group; + + /* The context ClassLoader for this thread */ + private ClassLoader contextClassLoader; + + /* The inherited AccessControlContext of this thread */ + private AccessControlContext inheritedAccessControlContext; + + /* For autonumbering anonymous threads. */ + private static int threadInitNumber; + private static synchronized int nextThreadNum() { + return threadInitNumber++; + } + + /* ThreadLocal values pertaining to this thread. This map is maintained + * by the ThreadLocal class. */ + ThreadLocal.ThreadLocalMap threadLocals = null; + + /* + * InheritableThreadLocal values pertaining to this thread. This map is + * maintained by the InheritableThreadLocal class. + */ + ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; + + /* + * The requested stack size for this thread, or 0 if the creator did + * not specify a stack size. It is up to the VM to do whatever it + * likes with this number; some VMs will ignore it. + */ +//栈的大小 + private long stackSize; + + /* + * JVM-private state that persists after native thread termination. + */ + private long nativeParkEventPointer; + + /* + * Thread ID + */ + private long tid; + + /* For generating thread ID */ + private static long threadSeqNumber; + + /* Java thread status for tools, + * initialized to indicate thread 'not yet started' + */ + + private volatile int threadStatus = 0; + + + private static synchronized long nextThreadID() { + return ++threadSeqNumber; + } + + /** + * The argument supplied to the current call to + * java.util.concurrent.locks.LockSupport.park. + * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker + * Accessed using java.util.concurrent.locks.LockSupport.getBlocker + */ + volatile Object parkBlocker; + + /* The object in which this thread is blocked in an interruptible I/O + * operation, if any. The blocker's interrupt method should be invoked + * after setting this thread's interrupt status. + */ + private volatile Interruptible blocker; + private final Object blockerLock = new Object(); +``` + +#### 主要方法 + +1.start方法 + +```java +public synchronized void start() { + /** + * This method is not invoked for the main method thread or "system" + * group threads created/set up by the VM. Any new functionality added + * to this method in the future may have to also be added to the VM. + * + * A zero status value corresponds to state "NEW". + */ + if (threadStatus != 0) + throw new IllegalThreadStateException(); + + /* Notify the group that this thread is about to be started + * so that it can be added to the group's list of threads + * and the group's unstarted count can be decremented. */ + group.add(this); + + boolean started = false; + try { + start0(); + started = true; + } finally { + try { + if (!started) { + group.threadStartFailed(this); + } + } catch (Throwable ignore) { + /* do nothing. If start0 threw a Throwable then + it will be passed up the call stack */ + } + } + } +``` + +​ start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。 + +2.run方法 + +```java +public void run() { + if (target != null) { + target.run(); + } + } +``` + +run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。 + +3.sleep方法 + +```java +sleep(long millis) //参数为毫秒 +sleep(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒 +``` + +sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。 + +  如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。 + +  当当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间内,该线程不会获得执行机会,即使系统中没有其他可执行线程,处于sleep()中的线程也不会执行,因此sleep()方法常用来暂停程序的执行 + +  但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。 + +4.yield方法 + +```java +public static native void yield(); +``` + +yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入到就绪状态。即让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。 + +  调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,当某个线程调用了yield()方法之后,只有优先级与当前线程相同或者比当前线程更高的处于就绪状态的线程才会获得执行机会。 + +  注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。 + +5.join方法 + +```java +join() +join(long millis) //参数为毫秒 +join(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒 +``` + +​ 假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的事件。 + +#### 构造方法 + +```java +//初始化一个线程 +private void init(ThreadGroup g, Runnable target, String name, + long stackSize, AccessControlContext acc, + boolean inheritThreadLocals) { + if (name == null) { + throw new NullPointerException("name cannot be null"); + } + + this.name = name; + + Thread parent = currentThread(); + SecurityManager security = System.getSecurityManager(); + if (g == null) { + /* Determine if it's an applet or not */ + + /* If there is a security manager, ask the security manager + what to do. */ + if (security != null) { + g = security.getThreadGroup(); + } + + /* If the security doesn't have a strong opinion of the matter + use the parent thread group. */ + if (g == null) { + g = parent.getThreadGroup(); + } + } +//只有要运行的目标 +public Thread(Runnable target) { + init(null, target, "Thread-" + nextThreadNum(), 0); + } + + + Thread(Runnable target, AccessControlContext acc) { + init(null, target, "Thread-" + nextThreadNum(), 0, acc, false); + } + + public Thread(ThreadGroup group, Runnable target) { + init(group, target, "Thread-" + nextThreadNum(), 0); + } + + public Thread(String name) { + init(null, null, name, 0); + } + + public Thread(ThreadGroup group, String name) { + init(group, null, name, 0); + } + + public Thread(Runnable target, String name) { + init(null, target, name, 0); + } + + public Thread(ThreadGroup group, Runnable target, String name, + long stackSize) { + init(group, target, name, stackSize); + } + +``` + +#### Thread的简单调用 + +```java +new Thread(){ + @Override + public void run() { + try { + zeroEvenOdd.odd(intConsumer); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); +``` + + + diff --git a/week_05/59/ThreadLocal.md b/week_05/59/ThreadLocal.md new file mode 100644 index 0000000..3017cf9 --- /dev/null +++ b/week_05/59/ThreadLocal.md @@ -0,0 +1,146 @@ +### ThreadLocal + +本文参考自博客:https://www.cnblogs.com/fsmly/p/11020641.html + +#### 简介 + +​ 多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。 + +  ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。 + +#### 原理 + +Thread类中有两个变量threadLocals和inheritableThreadLocals,二者都是ThreadLocal内部类ThreadLocalMap类型的变量,我们通过查看内部内ThreadLocalMap可以发现实际上它类似于一个HashMap。在默认情况下,每个线程中的这两个变量都为null,只有当线程第一次调用ThreadLocal的set或者get方法的时候才会创建他们(后面我们会查看这两个方法的源码)。除此之外,和我所想的不同的是,每个线程的本地变量不是存放在ThreadLocal实例中,而是放在调用线程的ThreadLocals变量里面(前面也说过,该变量是Thread类的变量)。也就是说,ThreadLocal类型的本地变量是存放在具体的线程空间上,其本身相当于一个装载本地变量的工具壳,通过set方法将value添加到调用线程的threadLocals中,当调用线程调用get方法时候能够从它的threadLocals中取出变量。如果调用线程一直不终止,那么这个本地变量将会一直存放在他的threadLocals中,所以不使用本地变量的时候需要调用remove方法将threadLocals中删除不用的本地变量。下面我们通过查看ThreadLocal的set、get以及remove方法来查看ThreadLocal具体实怎样工作的 + +#### 主要方法 + +1.set方法 + +```java +public void set(T value) { + //当前线程 + Thread t = Thread.currentThread(); + ////以当前线程作为key值,去查找对应的线程变量,找到对应的map + ThreadLocalMap map = getMap(t); + if (map != null) + //如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值 + map.set(this, value); + //如果map为null,说明首次添加,需要首先创建出对应的map + else + createMap(t, value); + } +//在当前map里设置一个值 +private void set(ThreadLocal key, Object value) { + + + Entry[] tab = table; + int len = tab.length; + int i = key.threadLocalHashCode & (len-1); + + for (Entry e = tab[i]; + e != null; + e = tab[i = nextIndex(i, len)]) { + ThreadLocal k = e.get(); + + if (k == key) { + e.value = value; + return; + } + + if (k == null) { + replaceStaleEntry(key, value, i); + return; + } + } + + tab[i] = new Entry(key, value); + int sz = ++size; + if (!cleanSomeSlots(i, sz) && sz >= threshold) + rehash(); + } +``` + +2.get方法 + +​ 在get方法的实现中,首先获取当前调用者线程,如果当前线程的threadLocals不为null,就直接返回当前线程绑定的本地变量值,否则执行setInitialValue方法初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现,都是判断当前线程的threadLocals变量是否为null,是则添加本地变量(这个时候由于是初始化,所以添加的值为null),否则创建threadLocals变量,同样添加的值为null。 + +```java +public T get() { + //(1)获取当前线程 + Thread t = Thread.currentThread(); + //(2)获取当前线程的threadLocals变量 + ThreadLocalMap map = getMap(t); + //(3)如果threadLocals变量不为null,就可以在map中查找到本地变量的值 + if (map != null) { + ThreadLocalMap.Entry e = map.getEntry(this); + if (e != null) { + @SuppressWarnings("unchecked") + T result = (T)e.value; + return result; + } + } + //(4)执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量 + return setInitialValue(); +} + +private T setInitialValue() { + //protected T initialValue() {return null;} + T value = initialValue(); + //获取当前线程 + Thread t = Thread.currentThread(); + //以当前线程作为key值,去查找对应的线程变量,找到对应的map + ThreadLocalMap map = getMap(t); + //如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值 + if (map != null) + map.set(this, value); + //如果map为null,说明首次添加,需要首先创建出对应的map + else + createMap(t, value); + return value; +} +``` + +3.remove方法 + +​ remove方法判断该当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量 + +```java +public void remove() { + //获取当前线程 + ThreadLocalMap m = getMap(Thread.currentThread()); + //不为空则删除 + if (m != null) + m.remove(this); + } +``` + +#### 总结 + +ThreadLocal不支持继承性:同一个ThreadLocal变量在父线程中被设置值后,在子线程中是获取不到的。(threadLocals中为当前调用线程对应的本地变量,所以二者自然是不能共享的) + +```java +public class ThreadLocalTest2 { + 4 + 5 //创建ThreadLocal变量 + 6 public static ThreadLocal threadLocal = new ThreadLocal<>(); + 7 + 8 public static void main(String[] args) { + 9 //在main线程中添加main线程的本地变量 +10 threadLocal.set("mainVal"); +11 //新创建一个子线程 +12 Thread thread = new Thread(new Runnable() { +13 @Override +14 public void run() { +15 System.out.println("子线程中的本地变量值:"+threadLocal.get()); +16 } +17 }); +18 thread.start(); +19 //输出main线程中的本地变量值 +20 System.out.println("mainx线程中的本地变量值:"+threadLocal.get()); +21 } +22 } +``` + +### 总结 + +Executors类: 主要用于提供线程池相关的操作。如果能够深入理解其中的源码原理,可以用这个类来管理我们的线程池。 \ No newline at end of file diff --git a/week_05/59/ThreadPoolExecutor.md b/week_05/59/ThreadPoolExecutor.md new file mode 100644 index 0000000..2ac0e18 --- /dev/null +++ b/week_05/59/ThreadPoolExecutor.md @@ -0,0 +1,206 @@ +### ThreadPoolExecutor + +#### ThreadPoolExecutor与Executors优缺点 + +使用Executors哪里有风险?有什么风险? +Executors 返回线程池对象的弊端如下: + +FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM(Out Of Memory)。 + +CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM(Out Of Memory)。 + +ThreadPoolExecutor是普通线程池类,它包含一些最基本的线程池操作相的方法实现。 + +#### 构造方法 + +```java + public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + Executors.defaultThreadFactory(), defaultHandler); + } + + public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + threadFactory, defaultHandler); + } + + public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + RejectedExecutionHandler handler) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + Executors.defaultThreadFactory(), handler); + } + + public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory, + RejectedExecutionHandler handler) { + if (corePoolSize < 0 || + maximumPoolSize <= 0 || + maximumPoolSize < corePoolSize || + keepAliveTime < 0) + throw new IllegalArgumentException(); + if (workQueue == null || threadFactory == null || handler == null) + throw new NullPointerException(); + this.acc = System.getSecurityManager() == null ? + null : + AccessController.getContext(); + this.corePoolSize = corePoolSize; + this.maximumPoolSize = maximumPoolSize; + this.workQueue = workQueue; + this.keepAliveTime = unit.toNanos(keepAliveTime); + this.threadFactory = threadFactory; + this.handler = handler; + } +``` + +ThreadPoolExecutor有四个构造方法,其中前三个都是调用最后一个,最后一个有7个参数,分别是:corePoolSize,maximumPoolSize,workQueue,keepAliveTime,threadFactory,handler,unit。 + +##### corePoolSize + +核心线程数。 + +当正在运行的线程数小于核心线程数是,来一个任务就创建一个核心线程;否则,任务来了先不创建任务,而是丢到任务队列中。 + +##### maximumPoolSize + +最大线程数。 + +当队列也满了,来一个任务才能创建一个非核心线程,但不能超过最大线程数。 + +##### keepAliveTime、unit + +线程保持空闲时间和单位。 + +默认情况下,这两个参数仅当正在运行的参数大于核心线程数才有用,只针对非核心线程数。 + +但是如果allowCoreThreadTimeOut被设置成了true,则对核心线程也有效。当任务队列为空时,线程保持多久才会销毁。主要是通过阻塞队列带超时的poll(timeout,unit)方法实现。 + +##### workQueue + +任务队列, + +这个队列必须是阻塞队列。 + +##### threadFactory + +线程工厂 + +默认使用的Executors工具类中的DefaultThreadFactory类,但是这个类有个缺点,因为创建的线程名称是自动生成的,无法自定义以区分不通的线程池,且他们都是非守护线程。 + +自定义一个线程工厂: + +自己实现一个ThreadFactory,然后把名称和是否是守护进程的参数传进来,参考netty中的默认线程工程或者google中的线程工厂。 + +##### handler + +拒绝策略。 + +拒绝策略是表示当队列满了且线程数也达到最大,这时候要新加任务,超过了线程池容纳的方法,这时候新的任务应该按什么方式来处理。 + +常用的拒绝策略有,丢弃当前任务,丢弃最老的任务,抛出异常,调用者自己处理等等。 + +默认的策略是抛出异常。调用者可以根据异常再进行二次处理。 + +#### 主要方法 + +1.执行一个方法 + +```java +public void execute(Runnable command) { + if (command == null) + throw new NullPointerException(); + /* + * Proceed in 3 steps: + * + * 1. If fewer than corePoolSize threads are running, try to + * start a new thread with the given command as its first + * task. The call to addWorker atomically checks runState and + * workerCount, and so prevents false alarms that would add + * threads when it shouldn't, by returning false. + * + * 2. If a task can be successfully queued, then we still need + * to double-check whether we should have added a thread + * (because existing ones died since last checking) or that + * the pool shut down since entry into this method. So we + * recheck state and if necessary roll back the enqueuing if + * stopped, or start a new thread if there are none. + * + * 3. If we cannot queue task, then we try to add a new + * thread. If it fails, we know we are shut down or saturated + * and so reject the task. + */ + int c = ctl.get(); + if (workerCountOf(c) < corePoolSize) { + if (addWorker(command, true)) + return; + c = ctl.get(); + } + if (isRunning(c) && workQueue.offer(command)) { + int recheck = ctl.get(); + if (! isRunning(recheck) && remove(command)) + reject(command); + else if (workerCountOf(recheck) == 0) + addWorker(null, false); + } + else if (!addWorker(command, false)) + reject(command); + } +``` + +#### 2.shutdown方法 + +```java +//关闭当前任务方法 +public void shutdown() { + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + checkShutdownAccess(); + advanceRunState(SHUTDOWN); + interruptIdleWorkers(); + onShutdown(); // hook for ScheduledThreadPoolExecutor + } finally { + mainLock.unlock(); + } + tryTerminate(); + } +//关闭一系列任务 +public List shutdownNow() { + List tasks; + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + checkShutdownAccess(); + advanceRunState(STOP); + interruptWorkers(); + tasks = drainQueue(); + } finally { + mainLock.unlock(); + } + tryTerminate(); + return tasks; + } +``` + +#### 总结 + +​ 我们可以使用 ThreadPoolExecutor 类中的构造方法手动创建线程池的实例, 从而可以根据我们的使用情况来指定参数,满足使用的同时又能规避风险。 + +​ 使用Executors类创建线程池与使用ThreadPoolExecutor类的区别就是使用ThreadPoolExecutor类可以自定义传入我们设置的线程池的参数,更加灵活。 + -- Gitee