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 - 线程执行器的更多相关文章

  1. Java Concurrency - 线程的基础操作

    创建线程 在 Java 中,创建线程有两种方式: 继承 java.lang.Thread 类,重写 run 方法. public class MyJob extends Thread { @Overr ...

  2. java并发之线程执行器(Executor)

    线程执行器和不使用线程执行器的对比(优缺点) 1.线程执行器分离了任务的创建和执行,通过使用执行器,只需要实现Runnable接口的对象,然后把这些对象发送给执行器即可. 2.使用线程池来提高程序的性 ...

  3. 深入浅出 Java Concurrency (35): 线程池 part 8 线程池的实现及原理 (3)[转]

    线程池任务执行结果 这一节来探讨下线程池中任务执行的结果以及如何阻塞线程.取消任务等等. 1 package info.imxylz.study.concurrency.future;2 3 publ ...

  4. 深入浅出 Java Concurrency (34): 线程池 part 7 线程池的实现及原理 (2)[转]

    线程池任务执行流程 我们从一个API开始接触Executor是如何处理任务队列的. java.util.concurrent.Executor.execute(Runnable) Executes t ...

  5. 深入浅出 Java Concurrency (33): 线程池 part 6 线程池的实现及原理 (1)[转]

    线程池数据结构与线程构造方法 由于已经看到了ThreadPoolExecutor的源码,因此很容易就看到了ThreadPoolExecutor线程池的数据结构.图1描述了这种数据结构. 图1 Thre ...

  6. 深入浅出 Java Concurrency (28): 线程池 part 1 简介[转]

    从这一节开始正式进入线程池的部分.其实整个体系已经拖了很长的时间,因此后面的章节会加快速度,甚至只是一个半成品或者简单化,以后有时间的慢慢补充.完善. 其实线程池是并发包里面很重要的一部分,在实际情况 ...

  7. 深入浅出 Java Concurrency (29): 线程池 part 2 Executor 以及Executors[转]

    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具.真正的线程池接口是ExecutorService. 下面这张图完整描述了线程 ...

  8. Java Concurrency in Practice 读书笔记 第十章

    粗略看完<Java Concurrency in Practice>这部书,确实是多线程/并发编程的一本好书.里面对各种并发的技术解释得比较透彻,虽然是面向Java的,但很多概念在其他语言 ...

  9. 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则

    转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...

随机推荐

  1. log4j中的MDC和NDC

    NDC和MDC NDC(Nested Diagnostic Context)和MDC(Mapped Diagnostic Context)是log4j种非常有用的两个类,它们用于存储应用程序的上下文信 ...

  2. php 处理高并发的思路

    1.nginx 服务器,提高网站服务器并发性能 2.控制大文件的下载,减少CPU的消耗. 3.对于sql查询做缓存. 4.静态页面文件缓存. 5.CND缓存静态文件, 6.反向代理到多个服务器,用来分 ...

  3. 超级终端和SecureCRT进行Console口的连接

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...

  4. 用shader使图片背景透明

    转自:http://blog.csdn.net/dawn_moon/article/details/8631783 好吧,终于抽时间写这篇文章了. 手头上有很多人物行走图,技能特效图等,但这些图都有个 ...

  5. 对PostgreSQL中tablespace 与 database, table的理解

    开始: 当前的tablesapce信息 pgsql=# select * from pg_tablespace; spcname | spcowner | spclocation | spcacl | ...

  6. HTTP Post Request using Apache Commons

    Demonstrates an HTTP Post using the Apache Commons HTTP library. Required Libraries: httpcore-4.1.ja ...

  7. Codeforces Round #274 (Div. 1) B. Long Jumps 数学

    B. Long Jumps Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/480/problem/ ...

  8. apicloud+融云实现即时通讯

    请尊重作者的辛勤劳动!!! 使用apicloud开发已经快2个月了,起初的目的就是为了实现安卓和苹果的兼容,属于一个试验项目,究竟apicloud是否能够满足公司的要求?最 终看来还是不错的,使用ap ...

  9. [ES6] 16. Object Enhancements

    Define object: var color = "blue"; var speed = 120; var car = {color, speed}; console.log( ...

  10. 详解C/C++函数指针声明 ( *( void(*)())0)();

     ( *( void(*)())0)(); float *pf; 这个声明的含义是*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针. float *g() , (*h)(); 表示*g()与( ...