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. HL7及PIX相关的测试工具

    最近在开发PIX项目时需要一些工具, 比如PIX各个Actor的测试工具, HL7消息的验证工具等等. 下面列下我找见的几个 必备工具. 1. http://hit-testing.nist.gov: ...

  2. Objective-C的singleton模式

    最近因为在ios应用开发中,考虑到一些公共方法的封装使用,就决定使用单例模式的写法了..不知道,Object-c中的单例模式的写法是否和java中的写法是否有所区别?于是阿堂从网上一搜,发现“ Obj ...

  3. do{...}while(0)的作用

    不是为了循环的while. 1.用于宏定义,保证宏一定按照想要的方式执行. #define   foo(x)     start(x); end(x) if(flag) foo(x); 扩展以后的结果 ...

  4. 超轻量级spring模板方案

    最近从事的工作是web方面的,主要j2ee,spring jsp这些内容,由于刚入门,很多的技术都不了解.所谓初生牛犊不怕虎,刚入门,各种不顺手,比如写jsp,总是重复很多的代码,各种不爽,然后就去看 ...

  5. iOS UIWebView加载时添加进度条

    标注:此框架仅适合UIWebView  对iOS8后新出的WKWebView不适用,当然,你可以尝试修改框架里的几个代理方法. 框架是:NJKWebViewProgress 导入头文件 #import ...

  6. Actions 动作集

    --> 移动鼠标到指定位置(先触发onMouseOver动作)        Actions action = new Actions(driver);        WebElement th ...

  7. UVa532 Dungeon Master 三维迷宫

        学习点: scanf可以自动过滤空行 搜索时要先判断是否越界(L R C),再判断其他条件是否满足 bfs搜索时可以在入口处(push时)判断是否达到目标,也可以在出口处(pop时)   #i ...

  8. [AngularJS] Introduction to ui-router

    Introduce to basic $stateProvider.state() with $stateParams services. Understand how nested router w ...

  9. JAVA static 作用

    static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念. 被static修饰的成员变量和成员方法独立于该类的任何 ...

  10. androisd wifi

    http://blog.csdn.net/yunjinwang/article/details/11968837 http://blog.csdn.net/yunjinwang/article/det ...