目录
前言
最近在面试,关于一些多线程的内容,细致的部分很容易就想不起来具体应该怎么回答。翻开之前的笔记本,感觉就在眼前,知识不去实践,就像是别人的。尤其是程序猿这行,源源不断的新技术,就不要做捡了芝麻丢了西瓜的人。
总结在博客上,也能再温习一下。用不用的先门清~
常用的四种线程池
newCachedThreadPool——可缓存线程池
描述:
- 如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
特点
- 线程的创建数量几乎没有限制。这样可灵活的往线程池中添加线程。
- 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程
- 要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class executor {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
}
newFixedThreadPool————指定线程数量
描述:
- 创建一个指定工作线程数量的线程池。
- 每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
优点:
- 提高程序效率和节省创建线程时所耗的开销
缺点:
- 线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class fixedThreadPool {
public volatile int i;
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(8000);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
运行结果:两个线程一起执行,隔8s切换
newSingleThreadExecutor————单线程的Executor
描述:
- 只创建唯一的线程来执行任务。保证任务按照指定顺序(FIFO,LIFO,优先级)执行。
- 如果这个线程异常结束,会有另一个取代它
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class singleThreadExecutor {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
运行:单线程运行
newScheduleThreadPool——定时线程池
描述:
- 支持定时及周期性任务执行。
栗子:
- 延迟3s执行示例代码
import java.sql.Time;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class scheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("延迟5秒执行");
}
},5, TimeUnit.SECONDS);
}
}
栗子:
- 延迟2秒后每5秒执行一次,定期执行示例代码
import java.sql.Time;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class scheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("延迟5秒执行");
}
},2,5,TimeUnit.SECONDS);
}
}
运行:
线程池七大参数
corePoolSize——核心线程最大数
maximumPoolSize——线程池最大线程数
keepAliveTime——空闲线程存活时间。
- 当一个非核心线程被创建,使用完归还给线程池
- 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由
keepAliveTime来设定
unit——空闲线程存活时间单位
- 这是keepAliveTime的计量单位
workQueue——等待队列
- 当线程池满了,线程就会放入这个队列中。任务调度再取出
jdk提供四种工作队列
- ArrayBlockingQueue——基于数组的阻塞队列,按FIFO,新任务放队尾
- 有界的数组可以防止资源耗尽问题。
- 当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。
- 如果队列已经是满的,则创建一个新线程,
- 如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
- LinkedBlockingQuene——基于链表的无界阻塞队列,按照FIFO排序
- 其实最大容量为Interger.MAX
- 由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,
- 因此使用该工作队列时,参数maxPoolSize其实是不起作用的
- SynchronousQuene——不缓存任务的阻塞队列
- 生产者放入一个任务必须等到消费者取出这个任务。
- 也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,
- 如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略
- PriorityBlockingQueue——具有优先级的无界阻塞队列
- 优先级通过参数Comparator实现。
threadFactory
- 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
handler——拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,就用到拒绝策略
jdk中提供了4中拒绝策略
- CallerRunsPolicy——主线程自己执行该任务
- 该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,否则直接抛弃任务。
- AbortPolicy——抛出异常
- 该策略下,直接丢弃任务,并抛出RejectedExecutionException异常
- DiscardPolicy——直接丢弃
- 该策略下,直接丢弃任务,什么都不做
- DiscardOldestPolicy——早删晚进
- 该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
线程池工作流程
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
- 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
- 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
- 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
- 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
- 如果队列满了,而且正在运行的线程数量等于 maximumPoolSize,那么线程池会抛出异常 RejectExecutionException。
- 当一个线程完成任务时,它会从队列中取下一个任务来执行。
- 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。