This article mainly introduces relevant information about the detailed explanation of examples of the Java Executor framework. Examples are provided here to help you learn and understand this part of the content. Friends in need can refer to
Java Executor Framework Detailed example explanation
Most concurrency is achieved through task execution.
There are generally two ways to perform tasks: serial and parallel.
class SingleThreadWebServer { public static void main(String[] args) throws Exception { ServerSocket socket = new ServerSocket(80); while(true) { Socket conn = socket.accept(); handleRequest(conn); } } } class ThreadPerTaskWebServer { public static void main(String[] args) throws Exception { ServerSocket socket = new ServerSocket(80); while(true) { final Socket conn = socket.accept(); Runnable task = new Runnable() { public void run() { handleRequest(conn); } }; new Thread(task).start(); } } }
Of course, both of the above methods have problems. The problem with single-threading is that the amount of concurrency will be a bottleneck. In the multi-threaded version, unlimited thread creation will lead to insufficient resources.
Executor Framework
A task is a set of logical work units, and threads are a mechanism that enables tasks to be executed asynchronously.
JDK provides the Executor interface:
public interface Executor { void execute(Runnable command); }
Although the Executor interface is relatively simple, it is the basis of the asynchronous task execution framework, which can support a variety of Different types of task execution strategies. It provides a standard way to decouple the task submission process from the execution process. Use Runnable to represent tasks. The implementation of Executor provides mechanisms such as life cycle support and statistics application management.
Executor is based on the producer-consumer model. The operation of submitting a task is equivalent to the producer, and the thread executing the task is equivalent to the consumer.
Executor-based WebServer examples are as follows:
public class TaskExecutorWebServer { private static final int NTHREADS = 100; private static final Executor exec = Executors.newFixedThreadPool(NTHREADS); public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(80); while (true) { final Socket conn = serverSocket.accept(); Runnable task = new Runnable() { @Override public void run() { handleRequest(conn); } }; exec.execute(task); } } }
In addition, you can implement your own Executor to control whether it is concurrent or parallel, such as the following code:
/** * 执行已提交的 Runnable 任务的对象。 * 此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。 * 通常使用 Executor 而不是显式地创建线程。 * * * @author renchunxiao * */ public class ExecutorDemo { public static void main(String[] args) { Executor executor = new ThreadExecutor(); executor.execute(new Runnable() { @Override public void run() { // do something } }); Executor executor2 = new SerialExecutor(); executor2.execute(new Runnable() { @Override public void run() { // do something } }); } } /** * 创建一个线程来执行 command * * @author renchunxiao * */ class ThreadExecutor implements Executor { @Override public void execute(Runnable command) { new Thread(command).start(); } } /** * 串行执行 command * * @author renchunxiao * */ class SerialExecutor implements Executor { @Override public void execute(Runnable command) { command.run(); } }
Thread pool
The thread pool is the resource pool of threads, which can be created through the static factory method in Executors.
newFixedThreadPool. Create a fixed-length thread pool, and create a thread each time a task is submitted. Until the maximum number of thread pools is reached, the size of the thread pool will no longer change.
newSingleThreadExecutor. Single thread pool.
newCachedThreadPool. A thread pool that scales with task size.
newScheduledThreadPool. Create a fixed-length thread pool to perform tasks in a delayed or timed manner.
#The JVM will only exit after all non-daemon threads have terminated. Therefore, if the Executor cannot be closed correctly, the JVM cannot end.
In order to solve the life cycle problem of execution services, there is a new interface ExecutorService that extends the Executor interface.
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); <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
The ExecutorService life cycle has three states: running, closed, and terminated. ExecutorService is running when initially created. The shutdown method will shut down gracefully: it will no longer accept new tasks and wait for the completion of already executed tasks (including those that have not yet started). The shutdownNow method will perform a rough shutdown: it will attempt to cancel all running tasks and will no longer start tasks in the queue that have not yet started. After all tasks are completed, it enters the terminated state.
Callable and Future
The Executor framework uses Runnable as the basic task representation. Runnable is a limited abstraction in that its run method cannot return a value and throws a checked exception.
Many tasks are actually delayed calculations, such as database queries and obtaining resources from the network. For these tasks, Callable is a better abstraction, which assumes that call will return a value and may throw an exception.
The tasks executed by Executor have four life cycle stages: creation, submission, start and completion. Since some tasks take a long time and may need to be canceled, in the Executor framework, tasks that have been submitted but have not been started can be canceled.
Future represents the life cycle of a task and provides corresponding methods to determine whether it has been completed or canceled, as well as to obtain the results of the task and cancel the task, etc.
The above is the detailed content of Examples of Executor framework in Java. For more information, please follow other related articles on the PHP Chinese website!