java并发编程实战 第六章 任务执行
一、在线程中执行任务
1、串行地执行任务
当服务器正在处理请求时,新的连接必须等待直到请求处理完毕。
如果请求阻塞时间过长,用户将认为服务器不可用。
2、显式地为任务创建线程
通过每个请求创建一个新的线程来提供服务,从而实现高响应性。
需要创建大量线程时:
- 线程生命周期开销非常高,线程创建、销毁需要代价
- 资源消耗,活跃的线程消耗系统资源,尤其是内存。如果可运行的线程数量多于可用处理器的数量,那么有些线程将闲置。
大量闲置线程占用内存,给垃圾回收带来压力,并在竞争cpu资源时产生性能开销。 - 稳定性。可创建线程数据存在一个限制。受JVM启动参数、Thread构造函数中请求栈大小、底层操作系统对线程的限制等。
在一定范围内,增加线程可以提高系统吞吐率,超过这个范围,在创建更多的线程只会降低程序的执行速度,甚至系统崩溃。
二、Executor框架
Executor提供了一种标准的方法将任务的提交过程与执行过程解耦,并用Runnable来表示任务。
public interface Executor { void execute (Runnbale command); }
提供了对生命周期的支持,以及统计信息收集、应用程序管理机制和性能监视等机制。
Executor基于生产者-消费者模式,提交任务相当于生产者,执行任务的线程相当于消费者。
1、线程池
管理一组同构工作线程的资源池。比“为每个任务分配一个线程”优势更:
- 通过重用现有的线程而不是创建新线程,
- 可以在处理多个请求时分摊创建线程和销毁的开销。不会等待线程创建而延迟任务执行。
- 创建足够的线程使处理器保持忙绿,防止过多线程相互竞争资源而使应用程序耗尽内存或失败
-
newSingleThreadPool:单线程的Executor,创建单个工作者线程来执行任务,如果这个线程异常结束,会创建另一个线程来替代。
可以确保任务在队列中串行执行。
- newFixedThreadPool:创建一个固定大小的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量。
- newCachedThreadPool:创建一个可缓存的线程池,如果线程池当前规模超过了处理需求时,将回收空闲线程;当需求增加时,课添加新的线程,线程池规模无限制
-
newScheduledThreadPool:创建一个固定大小的线程池,而且以延迟或定时的方式来执行任务,类似Timer
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
2、Executor的生命周期
ExecutorService接口添加了生命周期管理方法。
ExecutorService的生命周期有三种状态:运行、关闭、已终止。
在初始化创建时处于运行状态。
shutdown方法将执行平缓关闭过程:不再接受新任务,同时等待已久提交的任务执行完成,包括还未开始执行的任务。
shutdownNow方法执行粗暴关闭过程:长手取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务。
3、延迟任务与周期任务
三、找出可以利用的并行性
如果要使用Executor,必须将任务表述为一个Runnable。
1、携带结果的任务Callbale与Future
Executor框架使用Runnable作为其基本任务的表示形式。
Runnable存在问题:虽然run能写入到日志文件或者将结果放入某个共享的数据结构,但不能返回一个值或抛出一个受检查的异常。
许多任务实际上都是存在延迟计算--执行数据库查询、从网络上获取资源或计算某个复杂功能。
对于这种任务,Callable是一种更好的抽象:它认为主入口点将返回一个值,并可能抛出一个异常。
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
Executor执行任务有4个生命周期阶段:创建、提交、开始、完成。
在Executor框架中,已提交尚未开始的任务可以取消,对于已经开始执行的任务,只有当它们能响应中断时,才能取消。
Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java获取MySQL数据库数据
Java获取MySQL数据库数据 工具:Eclipse navicat MySQL MySQL连接驱动:mysql-connector-java-5.1.42.jar 加载驱动: 把下载好的的包导入工程项目中: 导入包 接着创建数据库: 在company的数据库下创建emp: 代码: package cn.iflytek.com; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class TestDemo { public static void main(String[] args) { //声明Connection对象 Connection con; //驱动程序名 String driver="com.mysql.jdbc.Driver"; //Url指向要访问的数据库名mydata String url="jdbc:mysql:...
- 下一篇
java并发编程实战 第八章 线程池的使用
一、在任务与执行策略直接的隐性耦合 Executor框架可以将任务的提交与任务的执行策略解耦。有些类型的任务需要明确地指定执行策略,包括: 依赖性任务 大多数行为正确的任务都是独立的:他们不依赖于其他任务的执行时序、执行结果或其他效果。当在线程池中执行独立的任务时,可以随意修改线程池大小和配置。如果提交给线程池的任务需要依赖其他任务,那就隐含的给执行策略带来了约束,此时必须小心维持执行策略,一般产生活跃性问题。 使用线程封闭机制的任务 与线程池相比,但线程的Executor能够对并发性作出更强的承诺,他们能确保任务不糊并发的执行,使你能够放宽代码对线程安全的要求。对象可以封闭在线程中,使得在线程执行任务访问对象时不需要同步。使用场景:任务执行Executor是单线程的 对响应时间敏感的任务 GUI应用程序对响应时间敏感,需要用户点击按钮后尽可能快反馈结果。如果一个运行时间较长的任务提交到单线程中,或将多个运行时间较长的任务提交到只包含少量线程的线程池中,那么将降低由改Executor管理服务的响应性。 使用ThreadLocal的任务 ThreadLocal使每个线程都可以拥有某个变量...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS7设置SWAP分区,小内存服务器的救世主
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8编译安装MySQL8.0.19