实验简介
对于原生的线程处理,通常面对一些复杂应用时,我们很容易陷入到线程的一些繁琐的细节当中,代码出问题的风险特别大。好在从JDK1.5版本开始,Java直接为我们提供了一个专门用于并发线程的包java.util.concurrent,帮助很多对多线程理解得不透彻的程序员更加轻松地使用多线程。
其中Eexecutor接口作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,并用Runnable来表示任务,同时还提供了对线程生命周期的支持,线程池的自动管理,以及统计信息收集,应用程序管理机制和性能监视等机制。
本节实验主要为大家介绍Executor框架中多线程的用法,为我们后续的性能测试脚本开发和场景设计中对于多线程这门关键技术提供更稳定的解决方案。
实验目的
1.理解Executor框架的核心体系结构及关键对象。
2.熟练运用Executor框架管理线程池。
3.熟练运用Executor框架处理多线程及并发策略。
实验流程
1.线程池。
对于数据库连接,我们经常听到数据库连接池这个概念。因为建立数据库连接时非常耗时的一个操作,其中涉及到网络I/O的一些操作。因此人们就想到通过一个连接池来管理与数据库的连接。需要连接的话,就从连接池里取一个。当使用完了,就“关闭”连接,这不是正在意义上的关闭,只是把连接放回到我们的池里,供其他人继续使用。所以对于线程,也有了线程池这个概念,其中的原理和数据库连接池是差不多的。
线程池的作用就是用来限制系统中使用线程的数量以及更好的使用线程。根据系统的运行情况,可以自动或手动设置线程数量,达到运行的最佳效果:配置少了,将影响系统的执行效率,配置多了,又会浪费系统的资源。用线程池配置数量,其他线程排队等候。当一个任务执行完毕后,就从队列中取一个新任务运行,如果没有新任务,那么这个线程将等待。如果来了一个新任务,但是没有空闲线程的话,那么把任务加入到等待队列中。这种线程池管理的方式可以更好地复用已经创建的线程,避免频繁的线程资源回收和新建,其执行效率也将更高。
2.Executor关键对象。
Executor | 一个接口,其定义了一个接收Runnable对象的方法executor。 |
ExecutorService | 线程池接口, 是一个比Executor使用更广泛的子类接口,并且提供了生命周期管理的方法。 |
ThreadPoolExecutor | ExecutorService的默认实现。 |
ScheduledExecutorService | 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。 |
ScheduledThreadPoolExecutor | 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。 |
3.创建单线程的线程池。
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
package com.woniuxy.thread;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
public class ExecutorDemo {
public static void main(String[] args) { ExecutorDemo exeDemo = new ExecutorDemo(); exeDemo.singlePool(); }
// 单线程线程池 public void singlePool() { // 创建一个单线程的线程池对象esSingle ExecutorService esSingle = Executors.newSingleThreadExecutor(); for (int i=0; i<10; i++) { // 调用execute方法执行线程。Runnable虽然是一个接口,无法直接实例化, // 但在内部匿名实例中实例化的同时给出了其run()方法的实现,这样是允许的。 esSingle.execute(new Runnable() { public void run() { System.out.println("当前执行线程为:" + Thread.currentThread().getName()); } }); } } } |
上述代码执行后,进程并不会结束,因为线程池并没有被回收,线程仍处理待命的状态。除非我们调用该线程池的shutdown()方法强制关闭当前线程池。
4.创建固定大小的线程池。
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,在提交新任务,任务将会进入等待队列中等待。如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
public void fixedPool() { ExecutorService esFixed = Executors.newFixedThreadPool(5); for (int i=0; i<10; i++) { esFixed.execute(new Runnable() { public void run() { System.out.println("当前执行线程为:" + Thread.currentThread().getName()); } }); } esFixed.shutdown(); // 强制关闭当前线程池 } |
5.创建可缓存的线程池。
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池的最大值是Integer的最大值(2^31-1)。
public void cachedPool() { ExecutorService esCached = Executors.newCachedThreadPool(); 版权所有,转载本站文章请注明出处:蜗牛笔记, http://www.woniunote.com/article/79
${comment['nickname']} ${comment['createtime']}
${comment.content}
${reply.nickname} 回复 ${comment.nickname}
${reply.createtime}
回复内容:${reply.content}
|