多线程
线程的四种创建方式:
继承Thread类
实现Runnable接口
使用线程池创建线程
实现Callable接口
线程池
1使用线程池的好处
避免了频繁创建和销毁线程造成的资源和时间的浪费
缺点:第一次创建线程时需要消耗大量的资源和时间
2实现线程池的函数
工具类Executors(JDK5以后出现)
ExecutorService service = Executors.newFixedThreadPool(3);
public interface ExecutorService
extends java. util. concurrent. Executor
(1)固定线程池 newFixedThreadPool
public static java. util. concurrent. ExecutorService newFixedThreadPool(int nThreads )
该函数为ExecutorService下的静态函数
ExecutorService service = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
创建了一个有3个线程的固定线程池
提交5个任务
显示结果如下:
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
(2)单一线程池 newSingleThreadExecutor
(3)缓冲线程池 newCachedThreadExecutor
(4)时间调度线程池 newScheculedThreadExecutor
阿里巴巴规定以上创建线程的方式在Java开发中不允许使用,因此不做细致讲解
在实际开发中使用ThreadPoolExecutor 创建线程池
3.ThreadPoolExecutor
3.1 参数
具有7个参数
corePoolSize 核心线程数
即使不工作也始终在线程池里的线程
maximumPoolSize 最大线程数
线程池中允许存在的最大线程,最大线程数=核心线程数+空闲线程数
keepAliveTime 空闲线程最大存活时间
空闲线程在被摧毁前等待新任务的最大存活时间
unit 空闲线程最大存活时间的单位
workQueue 任务队列
任务被执行前的等待队列,只有有任务被提交时才存在
threadFactory 线程工厂
创造线程的工厂,使用固定参数
handler 任务的拒绝执行策略
当提交的任务数目超出最大线程数和等待队列之和时,多余的任务被拒绝执行而执行的策略
包括四种执行策略:
AbortPolicy 中止
抛出RejectedExecutionException 异常,中止提交任务,一般为默认的参数,即在使用ThreadPoolExecutor创建线程时若输入5个或6个参数,默认为该执行策略(线程工厂参数也可不输入直接默认)
CallerRunsPolicy 调用者 运行
提交的任务由调用者直接执行,如主线程
DiscardOldestPolicy 丢弃 最旧的
将队列中等待时间最长的任务直接丢弃
DiscardPolicy
直接丢弃任务,不抛异常不执行操作
原版解释:(前面的我只是翻译了一下,个人英语水平有限)
corePoolSize – the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set
maximumPoolSize – the maximum number of threads to allow in the pool
keepAliveTime – when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating. unit – the time unit for the keepAliveTime argument
workQueue – the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.
threadFactory – the factory to use when the executor creates a new thread
handler – the handler to use when execution is blocked because the thread bounds and queue capacities are reached
3.2工作原理及示例
线程池任务执行
任务 --> 主线程 --已满--> 任务队列 --已满--> 空闲线程 --已满--> 执行拒绝策略
方法:
submit() 提交任务
shutdown() 销毁线程池,若线程池不销毁将一直存在
代码示例:
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,
3,
2,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 4 ; i++) {
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
threadPoolExecutor.shutdown();
}
结果:
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
这是没有异常的输出结果
将提交线程数改为2
结果:
pool-1-thread-1
pool-1-thread-1
发现都是由一个线程执行的
任务进入时等待队列未满,等待主线程执行的任务执行完,进入主线程执行
将提交线程数改为5(超过最大线程数+任务队列等待数)
结果:
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-1
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@6d6f6e28 rejected from java.util.concurrent.ThreadPoolExecutor@135fbaa4[Running, pool size = 3, active threads = 0, queued tasks = 0, completed tasks = 4]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at Demo.Demo.main(Demo.java:15)
只执行了4个任务,并抛出了异常
将执行策略改为CallerRunsPolicy,同时将提交任务数改为7
结果:
main
pool-1-thread-2
pool-1-thread-1
main
pool-1-thread-3
pool-1-thread-2
pool-1-thread-3
出现了线程复用执行的情况因此线程池执行了5个任务,由于拒绝执行策略主线程了2个任务