Java Concurrency - 线程执行器
Usually, when you develop a simple, concurrent-programming application in Java, you create some Runnable objects and then create the corresponding Thread objects to execute them. If you have to develop a program that runs a lot of concurrent tasks, this approach has the following disadvantages:
- You have to implement all the code-related information to the management of the Thread objects (creation, ending, obtaining results).
- You create a Thread object per task. If you have to execute a big number of tasks, this can affect the throughput of the application.
- You have to control and manage efficiently the resources of the computer. If you create too many threads, you can saturate the system.
Since Java 5, the Java concurrency API provides a mechanism that aims at resolving problems. This mechanism is called the Executor framework and is around the Executor interface, its subinterface ExecutorService, and the ThreadPoolExecutor class that implements both interfaces.
Creating a thread executor
The first step to work with the Executor framework is to create an object of the ThreadPoolExecutor class. You can use the four constructors provided by that class or use a factory class named Executors that creates ThreadPoolExecutor. Once you have an executor, you can send Runnable or Callable objects to be executed.
public class Main {
public static void main(String[] args) {
ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {
threadPool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " is running. " + new Date());
}
});
}
threadPool.shutdown();
}
}
The ThreadPoolExecutor class has four different constructors but, due to their complexity, the Java concurrency API provides the Executors class to construct executors and other related objects. Although we can create ThreadPoolExecutor directly using one of its constructors, it's recommended to use the Executors class.
In this case, you have created a cached thread pool using the newCachedThreadPool() method. This method returns an ExecutorService object, so it's been cast to ThreadPoolExecutor to have access to all its methods. The cached thread pool you have created creates new threads if needed to execute the new tasks, and reuses the existing ones if they have finished the execution of the task they were running, which are now available. The reutilization of threads has the advantage that it reduces the time taken for thread creation. The cached thread pool has, however, a disadvantage of constant lying threads for new tasks, so if you send too many tasks to this executor, you can overload the system. Use the executor created by the newCachedThreadPool() method only when you have a reasonable number of threads or when they have a short duration.
You also have printed some log messages with information about the executor. Specifically, you have used the following methods:
- getPoolSize(): This method returns the actual number of threads in the pool of the executor
- getActiveCount(): This method returns the number of threads that are executing tasks in the executor
- getCompletedTaskCount(): This method returns the number of tasks completed by the executor
- getLargestPoolSize(): This method returns the maximum number of threads that has been in the pool at a time
One critical aspect of the ThreadPoolExecutor class, and of the executors in general, is that you have to end it explicitly. If you don't do this, the executor will continue its execution and the program won't end. If the executor doesn't have tasks to execute, it continues waiting for new tasks and it doesn't end its execution. A Java application won't end until all its non-daemon threads finish their execution, so, if you don't terminate the executor, your application will never end.
To indicate to the executor that you want to finish it, you can use the shutdown() method of the ThreadPoolExecutor class. When the executor finishes the execution of all pending tasks, it finishes its execution. After you call the shutdown() method, if you try to send another task to the executor, it will be rejected and the executor will throw a RejectedExecutionException exception.
The ThreadPoolExecutor class also provides other methods related with the finalization of the executor. These methods are:
- shutdownNow(): This method shut downs the executor immediately. It doesn't execute the pending tasks. It returns a list with all these pending tasks. The tasks that are running when you call this method continue with their execution, but the method doesn't wait for their finalization.
- isTerminated(): This method returns true if you have called the shutdown() or shutdownNow() methods and the executor finishes the process of shutting it down.
- isShutdown(): This method returns true if you have called the shutdown() method of the executor.
- awaitTermination(long timeout, TimeUnit unit): This method blocks the calling thread until the tasks of the executor have ended or the timeout occurs. The TimeUnit class is an enumeration with the following constants: DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, and SECONDS. If you want to wait for the completion of the tasks, regardless of their duration, use a big timeout, for example, DAYS.
public static void main(String[] args) { ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) {
threadPool.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName() + " starts running. " + new Date());
try {
TimeUnit.SECONDS.sleep((long)(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " ends running. " + new Date());
}
});
} threadPool.shutdown(); try {
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All tasks are completed. " + new Date());
}
Creating a fixed-size thread executor
When you use basic ThreadPoolExecutor created with the newCachedThreadPool() method of the Executors class, you can have a problem with the number of threads the executor is running at a time. The executor creates a new thread for each task that receives, (if there is no pooled thread free) so, if you send a large number of tasks and they have long duration, you can overload the system and provoke a poor performance of your application.
If you want to avoid this problem, the Executors class provides a method to create a fixed-size thread executor. This executor has a maximum number of threads. If you send more tasks than the number of threads, the executor won't create additional threads and the remaining tasks will be blocked until the executor has a free thread. With this behavior, you guarantee that the executor won't yield a poor performance of your application.
public class Main {
public static void main(String[] args) {
ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int num = i + 1;
threadPool.execute(new Runnable() {
public void run() {
System.out.printf("No.%d begins running. %s\n", num, new Date());
try {
TimeUnit.SECONDS.sleep((long)(Math.random() * 10));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("No.%d ends running. %s\n", num, new Date());
}
});
}
threadPool.shutdown();
try {
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All tasks are completed. " + new Date());
}
}
In this case, you have used the newFixedThreadPool() method of the Executors class to create the executor. This method creates an executor with a maximum number of threads. If you send more tasks than the number of threads, the remaining tasks will be blocked until there is a free thread to process them. This method receives the maximum number of threads as a parameter you want to have in your executor. In your case, you have created an executor with three threads.
The following snippet shows part of the output of one execution of this example:
No.1 begins running. Sat Nov 05 11:02:25 CST 2016
No.0 begins running. Sat Nov 05 11:02:25 CST 2016
No.2 begins running. Sat Nov 05 11:02:25 CST 2016
No.0 ends running. Sat Nov 05 11:02:28 CST 2016
No.3 begins running. Sat Nov 05 11:02:28 CST 2016
No.3 ends running. Sat Nov 05 11:02:30 CST 2016
No.4 begins running. Sat Nov 05 11:02:30 CST 2016
No.1 ends running. Sat Nov 05 11:02:31 CST 2016
No.5 begins running. Sat Nov 05 11:02:31 CST 2016
No.5 ends running. Sat Nov 05 11:02:31 CST 2016
No.6 begins running. Sat Nov 05 11:02:31 CST 2016
No.6 ends running. Sat Nov 05 11:02:31 CST 2016
No.7 begins running. Sat Nov 05 11:02:31 CST 2016
No.2 ends running. Sat Nov 05 11:02:32 CST 2016
No.8 begins running. Sat Nov 05 11:02:32 CST 2016
No.8 ends running. Sat Nov 05 11:02:32 CST 2016
No.9 begins running. Sat Nov 05 11:02:32 CST 2016
No.4 ends running. Sat Nov 05 11:02:35 CST 2016
No.9 ends running. Sat Nov 05 11:02:39 CST 2016
No.7 ends running. Sat Nov 05 11:02:39 CST 2016
All tasks are completed. Sat Nov 05 11:02:39 CST 2016
The Executors class also provides the newSingleThreadExecutor() method. This is an extreme case of a fixed-size thread executor. It creates an executor with only one thread, so it can only execute one task at a time.
Java Concurrency - 线程执行器的更多相关文章
- Java Concurrency - 线程的基础操作
创建线程 在 Java 中,创建线程有两种方式: 继承 java.lang.Thread 类,重写 run 方法. public class MyJob extends Thread { @Overr ...
- java并发之线程执行器(Executor)
线程执行器和不使用线程执行器的对比(优缺点) 1.线程执行器分离了任务的创建和执行,通过使用执行器,只需要实现Runnable接口的对象,然后把这些对象发送给执行器即可. 2.使用线程池来提高程序的性 ...
- 深入浅出 Java Concurrency (35): 线程池 part 8 线程池的实现及原理 (3)[转]
线程池任务执行结果 这一节来探讨下线程池中任务执行的结果以及如何阻塞线程.取消任务等等. 1 package info.imxylz.study.concurrency.future;2 3 publ ...
- 深入浅出 Java Concurrency (34): 线程池 part 7 线程池的实现及原理 (2)[转]
线程池任务执行流程 我们从一个API开始接触Executor是如何处理任务队列的. java.util.concurrent.Executor.execute(Runnable) Executes t ...
- 深入浅出 Java Concurrency (33): 线程池 part 6 线程池的实现及原理 (1)[转]
线程池数据结构与线程构造方法 由于已经看到了ThreadPoolExecutor的源码,因此很容易就看到了ThreadPoolExecutor线程池的数据结构.图1描述了这种数据结构. 图1 Thre ...
- 深入浅出 Java Concurrency (28): 线程池 part 1 简介[转]
从这一节开始正式进入线程池的部分.其实整个体系已经拖了很长的时间,因此后面的章节会加快速度,甚至只是一个半成品或者简单化,以后有时间的慢慢补充.完善. 其实线程池是并发包里面很重要的一部分,在实际情况 ...
- 深入浅出 Java Concurrency (29): 线程池 part 2 Executor 以及Executors[转]
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具.真正的线程池接口是ExecutorService. 下面这张图完整描述了线程 ...
- Java Concurrency in Practice 读书笔记 第十章
粗略看完<Java Concurrency in Practice>这部书,确实是多线程/并发编程的一本好书.里面对各种并发的技术解释得比较透彻,虽然是面向Java的,但很多概念在其他语言 ...
- 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则
转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...
随机推荐
- Light oj 1236 - Pairs Forming LCM (约数的状压思想)
题目链接:http://lightoj.com/volume_showproblem.php?problem=1236 题意很好懂,就是让你求lcm(i , j)的i与j的对数. 可以先预处理1e7以 ...
- Javascript 原型继承(续)—从函数到构造器的角色转换
对于每一个声明的函数,里边都会带有一个prototype成员,prototype会指向一个对象,现在我们来聚焦prototype指向的这个对象,首先我们会认为,这个对象是一个该函数对应的一个实例对象, ...
- 小谈chrome调试命令:console.log的使用
相信从事前端开发的您,一定不会陌生Mozilla五星级推荐的一款插件:firebug,它是如此强大,乃至于我们可以很方便地调试DHTML的近乎所有元素.而在它深邃的机体里,还存有一个命令:consol ...
- delphi TTreeView组件遍历磁盘目录
TTreeView组件遍历磁盘目录 实例说明 TTreeView组件是一个以分枝结构或者说树状结构显示数据的组件,以该组件显示数据具有较好的等级关系和逻辑层次,并且易于操作.在组件中显示的数据结构与系 ...
- 浅谈Java对象回收的三种方式
半夜睡不着,加上最近在看Java虚拟机,写点给新手和自己看的东西. 第一类:生命周期中止 void scope(){ Test t = new Test(); } 第二类:对象无引用 (一).对象的应 ...
- Android学习笔记之百度地图
步行路线搜索及RouteOverlay 方式与驾车路线搜索类似,只需将mMKSearch.drivingSearch(null, start, null, end)修改为mMKSearch.walki ...
- Android进阶2之APK方式换肤
public class MainActivity extends Activity { private Button defaultbutton = null; @Override public v ...
- 判断IE中iframe完美加载完毕的方法
转: var iframe = document.createElement("iframe"); iframe.src = "http://www.planabc.ne ...
- Fedora 20 安装后的一些事情
1.关闭selinux 可以在软件中,找到selinux管理工具:system-config-selinux.py 2.安装源 可以通过# ls -l /etc/yum.repos.d 查看现有的安装 ...
- Asp.Net下载页面,并弹出下载提示框
Asp.Net下载页面,并弹出下载提示框.在删除按钮里调用以下方法.