Fifth, the task execution

一、在线程中执行任务

When the "mandate" to design applications around the structure, the first step is to find a clear mission boundaries. In the ideal case, between the respective tasks are independent of each other: the task does not depend on the state of other tasks, or the results of boundary effects. Help to achieve independence concurrency, because if there is more than enough processing resources, then these independent tasks can be executed in parallel. In order to achieve greater flexibility in scheduling and load balancing process, each task should also represents a fraction of the processing power of the application.
Under normal load, the server application should also show good throughput and rapid responsiveness. Application providers want the program to support as many users, thereby reducing the cost of service for each user, and the user wants to get a response as soon as possible. Further, when the load is overloaded, the performance of the application should be gradually reduced, rather than simply fail. To achieve these goals, we should choose a clear mandate and a clear boundary task execution strategy
most server applications provide a natural boundary task selection method: an independent customer requests for the border. Web server, mail server, file server, EJB container and database servers, these servers can accept remote client connection requests over the network. The independent task request as a boundary, either to achieve the independence of the task, but also
to achieve a reasonable scale of the task. For example, a submitted message to the result obtained by the mail server, and the other is not affected by the message being processed, but when dealing with a single message usually only a small fraction of total processing capacity of the server.

  • 串行地执行任务

In the application tasks can be scheduled through a variety of strategies, some of which strategy to better utilize the potential of concurrency. The simplest strategy is to serially perform various tasks in a single thread. Listing of serial SingleThreddWebServer handle its task (i.e., receives an HTTP request via the port 80). As for the details of how to handle requests, where is not important, we are interested in how to characterize different scheduling strategies synchronization features.

class SingleThreadWebServer {
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(80);
        
        while (true) {
            Socket connection = socket.accept();
            handleRequest(connection);
        }
    }
}

SingleThreadWebServer is very simple, and in theory is correct, but the actual execution performance in a production environment is very bad, because it can only handle one request. The main thread continues to run alternately accepting connections between processing and other operations related to the request. When the server is processing the request, the new incoming connection requests must wait until the process is completed, then the server calls accept again. If the processing speed of the request quickly and handleRequest can return immediately, then this method is feasible, but the situation in the real world of the Web server is not the case.

  • 显示地为任务创建线程

By for each request to create a new thread to provide services, in order to achieve greater responsiveness, as shown ThreadPerTaskWebServer program list.

class ThreadPerTaskWebServer {
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(80);

        while (true) {
            final Socket connection = socket.accept();
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    handleRequest(connection);
                }
            };

            new Thread(runnable).start();
        }
    }
}
  • 无线创建线程的不足

In a production environment, "assigned to each task a thread," this approach has some drawbacks, especially when you need to create a large number of threads:

  • 线程生命周期的开销非常高。Creating and destroying threads is not without a price. Depending on the platform, the actual costs are different, but the thread creation process will take time and delay processing of requests and requires JVM and operating system to provide some auxiliary operations. If the request arrival rate is very high and the processing of the request is lightweight, such as most server applications is the case, then create a new thread for each will consume a lot of computing resource requests.
  • 咨源消耗。Active threads consume system resources, especially memory. If the number of threads that can run more than the number of available processors, then some threads will be idle. A large number of idle threads will take up a lot of memory, to bring pressure on the garbage collector, and a large number of threads will also have other performance overhead in competing for CPU resources. If you already have enough threads to keep busy all CPU, then re-create more threads actually reduce performance.
  • 稳定性。There is a limit on the number of threads can be created. This limit will vary from platform to platform, and by a plurality of factors, including the JVM startup parameters, stack size Thread constructor request, and the underlying operating system and other restrictions on the thread. If the destruction of these restrictions, it is likely OutOfMemoryError is thrown, in order to recover from this error is very dangerous, simpler way is by constructing a program to avoid exceeding these limits
    within a certain range, it can increase the thread improve the throughput of the system, but if outside this range, then create more threads will reduce the execution speed of the program, and if too much to create a thread, then the entire application crashes. To avoid this danger, it should be the number of threads that can be created to limit the application, and will fully test the application to ensure that the number of threads when the limit is reached, the program will not run out of resources.
    "Assign each task a thread" The problem with this approach is that it does not limit the number of threads can be created only limit the rate at which a remote user to submit HTTP requests. As with other concurrent danger, prototype design and development stage, without limitation might be able to create threads to run better, but after the application is deployed and running under high load, will have problems constantly exposed. Therefore, a malicious user or too many users, will cause the Web server load reaches a certain threshold, so that the server crashes. If the server needs to provide Gao availability, and can gently reduce performance under high load conditions, then it would be a serious failure.

二、Executor框架

A task is a set of logical unit of work, and the thread is the mechanism for asynchronous task execution. We have analyzed two strategies to perform tasks by a thread, that is, all tasks on a single thread serial execution, and each task in its own thread execution. Both approaches, there are some strict limitations: 串行执行的问题在于其糟糕的响应性和吞吐 量,而“为每个任务分配一个线程”的问题在于资源管理的复杂性。
thread pool simplifies thread management, and java.util.concurrent provides a flexible thread pool Executor implemented as part of the frame. In the Java class libraries, a task performed mainly abstract than Thread, but Executor, as shown in Listing.

public interface Executor {
    void execute(Runnable command);
}

Although the Executor is a simple interface, but it provides the basis for flexible and powerful asynchronous task execution framework, the framework can support a variety of different types of task execution policy. It provides a standard approach to the task of decoupling submission process and the implementation process off, and is represented by Runnable task. Executor's implementation also provides support for life-cycle, and statistics collection, management mechanism and application performance monitoring mechanisms.
Executor based producer - consumer operating mode, the equivalent of producers to submit a task (generating units of work to be done), mission thread is equivalent to the consumer (you perform these work units). If you are implementing a program producer - consumer design, it is usually the easiest way is to use the Executor.

  • 示列:基于Executor的Web服务器
class TaskExecutionWebServer {
    private static final int NTHREADS = 100;
    private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);

    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(80);

        while (true) {
            final Socket connection = socket.accept();
            Runnable runnable = () -> handleRequest(connection);

            exec.execute(runnable); // 基于线程池执行
        }
    }
}
  • 执行策略

By submitting tasks to the implementation of decoupling open, so without too much difficulty can specify that certain types of tasks and
modifying execution policy. It defines the tasks performed "What, Where, When, How " in the implementation of the policy and other aspects, including:

  • In what (What) thread execution thousand each?
  • Tasks according to what (What) order execution (FIFO, LIFO, priorities)?
  • How many (HowManv) thousand works can be executed concurrently?
  • How many (HowMany) task awaiting execution in the queue?
  • If the system due to overload Ling cabinet must be a task, you should choose which one (Which) task? In addition, how (How) notification application has rejected task?
  • Before or after performing a task, what (What) action should be?
    Various execution strategies are a resource management tool, the best strategy depends on the available computing resources and service quality
    requirements. By limiting the number of concurrent tasks, ensure that your applications do not fail due to resource depletion, or due to competition over scarce resources and severely affect performance. Through the implementation of the policy will be submitted to the task and the task of separated, and the hardware resources available to help you choose the best match of the implementation strategy in the deployment phase.

每当看到下面这种形式的代码时:

new Thread(runnable).start()

并且你希望获得一种更灵活的执行策略时,请考虑使用Executor来代替Thread。

  • 线程池

Thread pool, from the literal meaning of the term, refers to a set of homogeneous management of the resource pool of worker threads. Thread pool is closely related to the work queue (Work Queue) which holds all the tasks waiting to be executed in the work queue. Worker threads (Worker Thread) task is simple: Get a job from the job queue, perform tasks, and then return to the thread pool and wait for the next task.
Generated in the process of creation and destruction of threads in the thread pool to perform tasks more "than the" thread for each task assigned a "advantage. Instead of creating a new thread by reusing existing threads, can be spread when processing multiple requests huge overhead. another added benefit is that when a request arrives, the worker usually already exist, it will not be delayed due to waiting for a thread is created to perform tasks, thereby improving the responsiveness. by properly adjusting the thread pool size, you can create enough threads to hold the processor busy, but also can prevent too many threads competing for resources with each other or the application to run out of memory failure.
library provides a flexible thread pool as well as some useful default configuration. by call one of the static factory methods in Executors to create a thread pool:

  • newFixedThreadPooL。newFixedThreadPool将创建一个固定长度的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量,这时线程池的规模将不再变化(如果某个线程由于发生了未预期的Exception而结束,那么线程池会补充一个新的线程)。
  • newCachedThreadPool。newCachedThreadPool将创建一个可缓存的线程池,如果线程池的当前规模超过了处理需求时,那么将回收空闲的线程,而当需求增加时,则可以添加新的线程,线程池的规模不存在任何限制。
  • newSingleThreadExecutor。newSingleThreadExecutor是一个单线程的 Executor,它创建单个工作者线程来执行任务,如果这个线程异常结束,会创建另一个线程来替代。newSingleThreadExecutor能确保依照任务在队列中的顺序来串行执行(例如FIFO、LIFO,优先级)。
  • newScheduledThreadPool。newScheduledThreadPool创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer 。
    newFixedThreadPool newCachedThreadPool both plants and general method returns ThreadPoolExecutor examples that can be directly used executor configured special purpose.
  • Executor的生命周期

Executor的实现通常会 创建线程来执行任务。但JVM只有在所有(非守护)线程全部终止后才会退出。因此,如果 无法正确地关闭Executor,那么JVM将无法结束。
为了解决执行服务的生命周期问题,Executor扩展了 ExecutorService接口,添加了一些用于生命周期管理的方法(同时还有一些用于任务提交的便利方法)。In Listing shows the life-cycle management of ExecutorService.

public interface ExecutorService extends Executor {
	void shutdown();
	List<Runnable> shutdownNow();
	boolean isShutdown();
	boolean isTerminated();
	boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
	<T> Future<T> submit(Callable<T> task);
	<T> Future<T> submit(Runnable task, T result);
	Future<?> submit(Runnable task);
	........
}

ExecutorService的生命周期有3种状态:运行、关闭和已终止。ExecutorService is running at the time of initial creation. shutdown method performs gentle closing process: no longer accept new tasks while waiting for task execution has been completed - including those submitted by the task has not yet started execution. shutdownNow method will be performed rowdy closing process: it attempts to cancel all running tasks, and task queue has not yet started no longer start.
ExecutorService tasks after closed filed by "refusal to implement processor (Rejected Execution Handler)" to deal with, it will abandon the task, or make execute method throws an unchecked Rejected- ExecutionException. Once all tasks are completed, ExecutorService will be transferred to the final state. You can call awaitTermination to wait ExecutorService reaches the end state, or by calling isTerminated to poll whether ExecutorService has been terminated. Usually after calling awaitTermination calls shutdown immediately, resulting in close synchronization with ExecutorService effect.

  • 延迟任务与周期任务

Delay Timer class is responsible for managing tasks ( "perform the task after 100ms") as well as periodic tasks ( "every 10ms to perform the task once"). However, Timer, there are some defects, so you should consider using ScheduledThreadPoolExecutorinstead. You can create objects of that class through the constructor ScheduledThreadPoolExecutor or newScheduledThreadPool factory method.
TimerWill create a thread in the implementation of all scheduled tasks. If the execution time of a task is too long, it will undermine the accuracy of the timing of other TimerTask. For example, a cycle TimerTaskneeds to be performed once every 10ms, 40ms and the other TimerTask need to be performed, then the task period or four times in quick succession to call in 40ms after the completion of task execution, or completely "lost" four times to call (depending on whether it is based on a fixed rate is to be scheduled based on a fixed schedule delay). Can make up for this thread pool dissatisfied depression, it can provide multiple threads to execute tasks delay and periodic tasks.
Timer Another problem is that if an exception is thrown TimerTask an unchecked, then the Timer will exhibit bad behavior. Timer thread does not catch the exception, therefore thrown when TimerTask unchecked timing of the thread terminates. In this case, Timer will not resume threads, but will mistakenly believe that the entire Timer are canceled. Therefore, TimerTask has been scheduled but not yet executed will no longer perform new tasks can not be scheduled. (This problem is called "thread leakage [Thread Leakage]".)

三、找出可利用的并行性

Executor framework to help specify the execution policy, but if you want to use Executor, the task must be expressed as a Runnable. In most server applications there is a significant task boundary: a single customer request. But sometimes, the task border is not obvious, such as in many desktop applications. Even the server application, there may still be excavated parallelism within a single client request, such as a database server.

  • 携带结果的任务Callable与Future

Runnable Executor framework uses as its basic task representation. Runnable is an abstract of a great limitations, although the run can be written to a log file or the results into a shared data structure, but it does not return a value or throw an exception under examination.
Perform database queries, access to resources from the network, or a complex computing functions - many tasks actually calculate the delay are present. For these tasks, Callable是一种更好的抽象:它认为主入口点(即call)将返 回一个值,并可能抛出一个异常。comprising a number of helper methods in the Executor capable of other types of tasks to a package a Callable, e.g. Runnable and java.security.PrivilegedAction.
Runnable和Callable描述的都是抽象的计算任务。这些任务通常是有范围的,即都有 一个明确的起始点,并且最终会结束。Executor task execution has four life-cycle stages: creation, submission, start and finish. As some tasks may be performed for a long time, so usually want to be able to cancel these tasks. In Executor framework has been submitted but not yet begun the task can be canceled, but for those who have started the task, only when they can interrupt response, in order to cancel. Cancel a task has been completed will not have any impact.
Future表示一个任务的生命周期,并提供了相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等。Callable and Future are given in the program list. In the Future specification contained implication is that the life cycle of the task can only move forward, not backward, just like ExecutorService the life cycle of the same. . When a task is completed, it will forever remain in the "completed" state.
get方法的行为取决于任务的状态(尚未开始、正在运行、已完成)。如果任务已经完成,那么get会立即返回或者抛出一个Exception,如果任务没有完成,那么get将阻塞并直到任务完成。If the task throws an exception, then get the anomaly and re-packaged as ExecutionException throw. If a task is canceled, it will get thrown CancellationException. If you get thrown ExecutionException, you can obtain an initial abnormal encapsulated by getCause.

public interface Callable<V> {
    V call() throws Exception;
}

public interface Future<V> {
    boolean cancel(boolean var1);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long var1, TimeUnit var3) throws InterruptedException, ExecutionException, TimeoutException;
}

You can create a Future in a number of ways to describe God's mission. All in all ExecutorService submit method returns a Future, which will be submitted to a Runnable or Callable Executor, and get a Future to obtain the execution result of a task or cancel the task. You may also be explicitly specified for a Callable Rmmable or instantiate a FutureTask. (Since FutureTask implements Runnable, so it can be delivered to the Executor to perform, or direct calls it nm methods.)
From the Java 6 start, ExecutorService achieve newTaskFor method AbstractExecutorService may be rewritten, thereby according to Runnable or Callable submitted examples of control process of the Future. In the default implementation only created a new FutureTask.

  • CompletionService、Executor 与 BlockingQueue

If a set of computational tasks submitted to the Executor, and the desired result is obtained after the calculation, it can be retained Future associated with each task, and then repeatedly using the get method, while the specified timeout parameter is 0, thereby determining by polling the task is complete. Although this method is feasible, but somewhat cumbersome. Fortunately, there is a better way:完成服务(CompletionService)。
CompletionService 将 Executor 和 BlockingQueue 的功能融合在一起。你可以将 Callable 任 务提交给它来执行,然后使用类似于队列操作的take和poll等方法来获得已完成的结果,而这 些结果会在完成时将被封装为 Future。ExecutorCompletionService 实现了 CompletionService,并将计算部分委托给一个Executor。

  • 为任务设置时限

Sometimes, if a task can not be completed within the specified time, then the result will not need it, then you can give up this task. For example, a Web application is available on the external ad server information, but if the application can not get a response within two seconds, it will display a default ad, so even if the ad can not get information, it will not reduce the response performance of the site. Similarly, a portal can obtain data from multiple data sources in parallel, but may only waiting for data within a specified period of time, if the waiting time elapsed, then display only the data that has been obtained.
The main difficulty performing tasks in the limited time that you want to make sure that time will not get an answer exceeds the allotted time, or within a limited time can not get an answer. In support of this demand Future.get time limit in support: When the results are available, it will return immediately if no results are calculated within a specified time, then it will throw TimeoutException.

Guess you like

Origin blog.csdn.net/qq_27870421/article/details/90582848