在组建java程序时候关于任务执行方面第一个要考虑的就是任务边界的划分,即把执行的程序分成合理的任务,理想的情况是这些任务的状态,结果不会影响到其它任务。

以java最擅长的服务器端开发为例子,服务器端要做到两点:高并发(good throughput)、低延迟(good responsiveness)。高并发是服务提供者的需要,我们想让我们的程序承载更多的用户来分担服务器开销;低延迟是用户的需求,用户想尽快的看到服务器对请求的响应结果。同样服务器的任务划分也是比较清晰和明确的:我们可以把用户的一个请求,作为一个任务。在高并发、低延迟的要求下,我们来设计下服务器的并发框架。
首先能想到的是单线程模式,程序如下:

public class SingleThreadWebServer{public static void main(String[] args) throws Exception{ServerSocket server = new ServerSocket(8900);while(true){Socket socket = server.accept(); handlerRequest(socket);}}
}

这个程序中,每个客户端请求和其处理都在一个程序中执行,在handlerRequest方法比较耗时的情况下,用户将感觉到卡顿,没能做到低延迟;同时因为同一时间只能有一个客户端请求被处理,服务器也没能做到高并发。在真实的生产环境中,除了GUI程序,基本上不会用到这种模型框架。

因为handlerRequest方法可能造成延迟,我们改进成程序如下:

public class ThreadPerTaskWebServer{public static void main(String[] args){ServerSocket serverSocket = new ServerSocket();while(true){Socket socket = serverSocket.accpet();new Thread(){@Overridepublic void run(){handlerReqeust(socket);}}.start();}}
}

此模式下将任务接受和任务执行分别放在不同的线程中来执行,main方法所在的线程主要负责接受客户端请求,具体的任务执行放在单独的一个新的线程中来执行。此模式可以很好的满足服务器高并发,低延迟的设计需求。但还是存在三个缺点:
1、线程生命周期成本太大(thread lifecycle overhead):此模式中,每个请求都要创建一个新的处理线程,当任务处理完毕后,此线程还要被销毁。但是线程的创建和销毁并不是没有成本的。线程的创建需要向jvm申请一些资源,这就给处理客户端请求带来了一些延迟。如果需要此线程处理的任务都是一些短时间的任务,那么线程的创建时间将会称为主要的延迟。
2、资源的消耗严重(Resource consumption):线程是需要消耗资源的,比如内存。当线程的数量超过了处理器的核数,多余的线程将阻塞,这将造成资源的浪费同时给gc造成极大的压力,同时线程之间竞争CPU也会造成一些性能上的牺牲。
3、影响程序稳定性(stability):程序能创建多少线程是有限制的,如果没有限制的创建过多的线程,很可能会得到OutOfMemoryError。

造成上述问题的根本原因是系统没有对程序中可创建的线程数量做限制,对上述程序改进如下:

public class TaskExecutionWebServer{private static Executor executor = new Exectutors.newFixedThreadPool(100); //1public static void main(String[] args){ServerSocket serverSocket = new ServerSocket(9090);while(true){Socket socket = serverSocket.accpet();Runnable task = new Runnable(){public void run(){handlerRequest(socket);}}executor.execute(task);}}
}

Executor 是一个接口,其只有一个方法execute。execute方法用来执行Runnable实例的,我们只需要把我们的handlerRequest方法的方法封装在一个Runnable实例中,然后把这个任务提交给Executor,具体的任务运行管理,交给Executor即可。同时我们在//1处可以看到我们最多可以创建
的线程数量被限定为100个。这就没有了ThreadPerTaskWebServer中的无限制创建线程的问题了。同时把任务的提交和任务的执行解耦还给我们带来了灵活的执行任务策略。我们可以很容易的把执行策略编程单线程模式,只需要把 //1处 exectuor变成一个OneThreadExecutor实例:

public class OneThreadExecutor implements Executor {public void execute(Runnable task){task.run();}
}

我们还可以把执行策略变成我们之前的一个任务一个线程的模式,只需要把 //1处 executor变成ThreadPerTaskExecutor实例:

public class ThreadPerTaskExecutor implements Executor{public void execute(Runnable task){new Thread(task).start();}
}

使用Executor接口来执行任务,可以让我们在获得了高并发,低延迟的前提下,又有了很大的灵活性,很方便的更改执行策略。但是考虑一个程序完整的生命周期,不应该只有执行状态,还应该有关闭状态。当程序要关闭的时候,Executor应该能对其做出相应的响应,所以一个Executor也应该有自己的生命周期。为了描述Executor的生命周期,我们引进了ExecutorService接口,其继承了Executor接口。一个ExecutorService实例有三种状态:running、shutting down、terminated。ExecutorService实例在刚刚创建的时候就处在running状态;当调用shutdown方法的时候,ExecutorService实例会把状态置为shutting down,同时把正在执行的任务继续执行,当通过execute方法向其中添加新的任务时,ExecutorService实例可以忽略新添加的任务,或者抛出一个异常,具体取决于策略的选取,这在以后再说。实例中没有正在执行的任务的时候,实例就进入terminated状态。一个具有生命周期管理的WebServer服务器类如下:

public class LifeCycleWebServer{private final ExecutorService exec = ....;public static void main(String[] args) throws Exception{ServerSocket socket = new ServerSocket(9090);while(!exec.isShutdown()){Socket socket = socket.accpet();exec.execute(new Runnable(){public void run(){if(isShutdownRequest(socket)){exec.shutdown();}handleRequest(socket);}};)}}
}

在LifeCycleWebServer类中,我们可以通过一个特殊的客户端请求来让ExecutorService进入关闭状态。这样ExecutorService实例也有了生命周期,更能适合真实的开发需求。

通过以上比较可知,用Executor框架执行任务的好处如下:
1、能够实现高并发和低延迟。
2、因为Executor的实现类可以很好的管理其线程的创建数量,可能任务提交给Executor的时候,已经有可以执行此任务的线程了,这样减少了
线程创建带来的延迟。因为Executor实例限制了,创建线程的数量,减少了线程之间对CPU的竞争,也在一定程度上提高了系统性能。对线程创建数量的限制,保证了系统的稳定性。
3、更加灵活的执行策略,因为Executor实现了任务的提交和执行的解耦,可以让我们灵活的更换任务的执行策略。
4、继承Executor接口的ExecutorService增加了生命周期的管理,更加贴近真实的生产开发。

综上所述,在java中执行任务首要考虑的是Executor,而不是Thread.
,可以让我们灵活的更换任务的执行策略。
4、继承Executor接口的ExecutorService增加了生命周期的管理,更加贴近真实的生产开发。

综上所述,在java中执行任务首要考虑的是Executor,而不是Thread.

java中用Executor代替Thread的四大理由相关推荐

  1. Java的Executor框架和线程池实现原理

    一,Java的Executor框架 1,Executor接口 public interface Executor {void execute(Runnable command);} Executor接 ...

  2. 怎么在java中关闭一个thread

    怎么在java中关闭一个thread 我们经常需要在java中用到thread,我们知道thread有一个start()方法可以开启一个线程.那么怎么关闭这个线程呢? 有人会说可以用Thread.st ...

  3. executor线程池框架_如何使用Java 5 Executor框架创建线程池

    executor线程池框架 Java 5以Executor框架的形式在Java中引入了线程池,它允许Java程序员将任务提交与任务执行分离. 如果要使用Java进行服务器端编程,则线程池是维护系统可伸 ...

  4. 如何使用Java 5 Executor框架创建线程池

    Java 5以Executor框架的形式在Java中引入了线程池,它允许Java程序员将任务提交与任务执行分离. 如果要使用Java进行服务器端编程,则线程池是维护系统可伸缩性,鲁棒性和稳定性的重要概 ...

  5. thread java 关闭_怎么在java中关闭一个thread

    怎么在java中关闭一个thread 我们经常需要在java中用到thread,我们知道thread有一个start()方法可以开启一个线程.那么怎么关闭这个线程呢? 有人会说可以用Thread.st ...

  6. 使用Executor管理Thread对象详解

    java SE5的java.util.concurrent包中的执行器(Executor)是管理Thread对象的优选方法.使用Executor管理Thread对象可以简化并发编程. Executor ...

  7. Java并发编程:Thread类的使用

    为什么80%的码农都做不了架构师?>>>    Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学 ...

  8. JAVA中用 SQL语句操作小结

    1.添加记录(INSERT) 使用SQL语句的INSERT命令可以向数据库中插入记录,INSERT命令的基本形式为: INSERT INTO 表名 [(字段名1,字段名2-)] VALUES (值1, ...

  9. 新媒体学python有用吗_你真的不学Python吗?学习Python的四大理由!

    在众多人的脑海中,Python无非就是一门编程语言而已,并没有什么特色,但是提及学习编程大部分人都会推荐Python,为什么?今天就给你说说学习Python的四大理由吧. 首先先来了解一下什么是Pyt ...

  10. 学python还是不会编程_你真的不学Python吗?学习Python的四大理由!

    在众多人的脑海中,Python无非就是一门编程语言而已,并没有什么特色,但是提及学习编程大部分人都会推荐Python,为什么?今天就给你说说学习Python的四大理由吧. 首先先来了解一下什么是Pyt ...

最新文章

  1. tx2 undefined reference to PyExc_ImportError'
  2. Java中的volatile关键字
  3. boost:is_straight_line_drawing用法的测试程序
  4. lnmp基于fastcgi实现nginx_php_mysql的分离_LNMP基于FastCGI实现Nginx,PHP,MySQL的分离
  5. windows安装zabbix客户端
  6. 重启用reboot后起不来_2021年中国“天眼”开放,美媒记者探访后感叹了……
  7. 【转】Android 最火框架XUtils之注解机制详解
  8. php 公用方法,Laravel配置全局公共函数的方法步骤
  9. 工业通讯 | OEM嵌入式通讯模块与西门子PLC S7-1200通讯测试指南
  10. 使用 Spring Data JPA 简化 JPA 开发
  11. Web安全—information_schema数据库详解基础补充
  12. java计算某国个人所得税税率表_计算个人所得税(新版)
  13. C语言实现飞机售票系统
  14. MapReduce之week2 test 分区计算结余(练习)
  15. 深入浅出 RPC - 浅出篇
  16. 一年收入8000万美金,美华国际医疗凭借“口罩”能否撑起IPO?
  17. qq授权登录【网站应用】-java版本
  18. 【NLP】AutoRegressive Language Model
  19. opencv 级联分类器
  20. Freckles - 九度 OJ 1144

热门文章

  1. Android 编程下 ListView 的 HeaderView 和 FooterView 不可选择点击
  2. (转)汉字转拼音码缩写
  3. Firefox6 使用 firebug 解决方法 以及迅雷(thunder)插件报错
  4. 一起学Android之Intent
  5. 20172304 2017-2018-2 《程序设计与数据结构》第八周学习总结
  6. 偏差、方差和噪声的权衡关系
  7. 老男孩教育每日一题-第106天-MySQL如何授权用户admin:password远程访问权限
  8. Oracle 11g R1(11.1) Joins表连接
  9. 虚拟机的性能测试经验总结(一)
  10. 如何配置和使用Tomcat访问日志