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 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...
随机推荐
- python dict{}和set([])
200 ? "200px" : this.width)!important;} --> 介绍 dict(dictionary),在其他语言中也称为map,使用键-值(key- ...
- cocos2d-x CCScale9Sprite
转自:http://www.cocos2dev.com/?p=295 前段时间看CCEditBox的时候,发现里面有个利用9宫格图缩放图片的,也就是缩放带圆角的图片. 这个比较有用处,很多游戏中有很多 ...
- Kerberos和NTLM - SQL Server
当我们使用Windows Authentication去连接SQL Server的时候,SQL Server可能会使用Kerberos或者是NTLM来进行认证,有时间就会因为认证失败的缘故造成各种登录 ...
- WinDbug之DUMP蓝屏分析
Microsoft (R) Windows Debugger Version 6.2.8400.0 X86Copyright (c) Microsoft Corporation. All rights ...
- Codeforces GYM 100114 B. Island 水题
B. Island Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Description O ...
- C#的WebBrowser操作frame如此简单
刚学c#不久,也不太懂什么IHTMLDocument.IHTMLDocument2.IWebBrowser2等等.自己琢磨了好久,终于知道了怎么用WebBrowser操作frame和iframe. 1 ...
- 使用QQ账号的来发送邮件
第一步,看图操作如下: 打开QQ邮箱,在设置-帐户,然后再看下图示: 把:POP3/SMTP服务,IMAP/SMTP服务,Exchange服务,CardDAV/CalDAV服务都选择上,保存这样,便可 ...
- codeforce447 D SGU 548 贪心+优先队列
codeforce447 D - DZY Loves Modification 题意:有一个n*m的矩阵,每次可以选择一行或者一列,可以得到这行或这列的所有元素sum的积分,然后使这一列/行的每一个元 ...
- 几种server模型
TCP測试用客户程序 每次执行客户程序,在命令行參数指定server的ip地址,port,发起连接的子进程数,和一个待发送的字符串数据,客户程序将模拟多个客户依据指定的子进程数创建子进程来并发的连接到 ...
- java中接口的定义与实现
1.定义接口 使用interface来定义一个接口.接口定义同类的定义类似,也是分为接口的声明和接口体,当中接口体由常量定义和方法定义两部分组成.定义接口的基本格式例如以下: [修饰符] in ...