JAVA concurrent programming 8 _ Use of thread pool, java thread

Source: Internet
Author: User

JAVA concurrent programming 8 _ Use of thread pool, java thread
Do not use Thread Pool

1. execute tasks in sequence

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

You need to wait for a connection to be processed before processing the next user request. The high throughput and fast response speed cannot be provided, and the cpu utilization is low.

2. Start a new thread for each request

class ThreadPerTaskWebServer {public static void main(String[] args) {ServerSocket socket = new ServerSocket(80);while (true) {final Socket conn = socket.accept();Runnable runnable = new Runnable() {@Overridepublic void run() {handleRequest(conn);}};new Thread(runnable).start();}}}

Enable a new thread for each user request and separate the task processing from the main thread so that the task can be processed in parallel. Improves the performance and response speed of serial execution.

Disadvantages:

The thread life cycle overhead is very high. The creation and destruction of threads are both costly, and the overhead of different platforms is also different.

Resource consumption. Too many threads consume system resources, such as memory usage of Idle threads, and other performance overhead generated when a large number of threads compete for CPU resources.

Stability. The number of threads that can be created is limited.

Executor framework

A task is a group of logical units of work, while a thread is a mechanism for asynchronous execution of the task. Both methods have some strict restrictions: the problem of serial execution is poor responsiveness and throughput, while the problem of "allocating a thread for each task" is the complexity of resource management.

The java. util. concurrent package provides flexible thread pool implementation as part of the Executor framework

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

Executor is a simple interface, but it provides the foundation for a flexible and powerful asynchronous task execution framework. It supports multiple task execution policies of different types. It provides a standard method to decouple the task submission process and execution process, and use Runnable to represent the task. Executor also supports the lifecycle, statistical information, application management, and performance monitoring.

Web Server Based on Thread Pool

public 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 server = new ServerSocket(80);        while (true){            final Socket connection = server.accept();            Runnable task = new Runnable() {                @Override                public void run() {                    handlerRequest(connection);                }            };            exec.execute(task);        }    }}

With the framework provided by Executor, We can modify TaskExecutionWebServer as a single thread (serial execution) behavior similar to SingleThreadWebServer.

public class SingleThreadExecutor implements Executor{@Overridepublic void execute(Runnable command) {command.run();}}

Similarly, we can modify TaskExecutionWebServer as a multi-thread (starts a thread for each request) behavior similar to ThreadPerTaskWebServer.

public class ThreadPerTaskExecutor implements Executor{@Overridepublic void execute(Runnable command) {new Thread(command).start();}}

You can use the static factory method in Executor to create a thread pool.

Creates a fixed-length thread pool. Each time a task is submitted, a thread is created until the maximum number of threads is reached. If a thread exception occurs, a new thread pool is created.

public static ExecutorServicenewFixedThreadPool(int nThreads) {...}

Create a single thread to execute the task. If this thread encounters an exception, a new thread is created. This pool can execute tasks (such as FIFO, LIFO, and priority) in the queue in order)

public static ExecutorServicenewSingleThreadExecutor() {...}

The thread pool has no length limit. It will be recycled when too many threads exist and created when too few threads exist.

public static ExecutorServicenewCachedThreadPool() {...}

Creates a fixed-length thread pool and executes tasks in a delayed or scheduled manner.

public static ScheduledExecutorServicenewScheduledThreadPool{...}

Executor is the top-level interface

ExecutorService inherits from Executor and adds some life cycle management methods and task submission methods.

AbstractExecutorService implements the ExecutorService interface. ThreadPollExecutor inherits from AbstractExecutorService.

Callable and Future

Runnable has many limitations. You cannot return a value or throw a checked exception. At this time, Callable can be competent. Callable can be used to return the running results of the goods thread. This result is obtained through the get method in Fuuture.

Future indicates the life cycle of a task, and provides methods to determine whether the task has been completed or canceled, as well as to obtain the task result and cancel the task. The action of the get method depends on the status of the task. If the task has been completed, get returns immediately or throws an Exception. If the task is not completed, get will be blocked until the task is completed. If a task throws an exception, get also throws an exception.

public interface Callable<V> {    V call() throws Exception;}public interface Future<V> {    boolean cancel(boolean mayInterruptIfRunning);    boolean isCancelled();    boolean isDone();    V get() throws InterruptedException, ExecutionException;    V get(long timeout, TimeUnit unit)        throws InterruptedException, ExecutionException, TimeoutException;}

You can create a Future to describe the task in multiple ways. All the submit methods in ExecutorService will return a Future, so that a Runnable or Callable is submitted to the Executor, and a Future is obtained to obtain the task execution result or cancel the task.

class MyCallable implements Callable<String> {@Overridepublic String call() {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}return "haha-callable";}}public class TestExecutor {public static void main(String[] args) {List<Future<String> > futureList = new ArrayList<Future<String>>();ExecutorService exec = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {futureList.add(exec.submit(new MyCallable()));}for (Future<String> future : futureList) {try {System.out.println(future.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}}}

Output

haha-callablehaha-callablehaha-callablehaha-callablehaha-callable

You can also explicitly instantiate a FutureTask for a specified Runnable or Callable. The FutureTask implements Runnable, so it can be executed by the Thread or Executor.

class MyCallable implements Callable<String> {@Overridepublic String call() {try {Random r = new Random();TimeUnit.SECONDS.sleep(r.nextInt(5));} catch (InterruptedException e) {e.printStackTrace();}return "haha-callable";}}public class TestExecutor {public static void main(String[] args) {List<Future<String> > futureList = new ArrayList<Future<String>>();ExecutorService exec = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {FutureTask<String> future = new FutureTask<String>(new MyCallable());futureList.add(future);new Thread(future).start();}for (Future<String> future : futureList) {try {System.out.println(future.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}}}

As in the preceding example, a group of tasks are submitted to Executor and you want to obtain the results after the computation is complete. You can keep the Future associated with each task according to the above method, and then use the get method in the for loop. The problem is that the get method will block waiting for the returned result. Therefore, the computing results will be waited according to the order in which the task is added.

For example, if the first task to be added takes 5 s, the second task takes 4 s... In the fifth 1 s, the calculation results of the five tasks cannot be obtained according to the value of the first executed task, because the get method will be blocked until the task is completed.

class MyCallable implements Callable<String> {private int id;public MyCallable(int id) {this.id = id;}@Overridepublic String call() {try {TimeUnit.SECONDS.sleep(5-id);} catch (InterruptedException e) {e.printStackTrace();}return id + " -- haha-callable";}}public class TestExecutor {public static void main(String[] args) {List<Future<String> > futureList = new ArrayList<Future<String>>();ExecutorService exec = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {futureList.add(exec.submit(new MyCallable(i)));}for (Future<String> future : futureList) {try {System.out.println(future.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}}}

It will be printed immediately after 5 seconds, and will not be printed first.

0 -- haha-callable1 -- haha-callable2 -- haha-callable3 -- haha-callable4 -- haha-callable

In fact, you can keep the Future associated with each task according to the above method, and use the get method repeatedly in the for loop, set timeout to 0, or determine whether the isDone of the current task is to get again, the Round-Robin method is used to determine whether the task is completed. This method is feasible but cumbersome.

class MyCallable implements Callable<String> {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");private int id;public MyCallable(int id) {this.id = id;}@Overridepublic String call() {try {TimeUnit.SECONDS.sleep(5-id);} catch (InterruptedException e) {e.printStackTrace();}return df.format(new Date()) +" " + id + " -- haha-callable";}}public class TestExecutor {public static void main(String[] args) {List<Future<String> > futureList = new ArrayList<Future<String>>();List<Future<String> > finishedFutureList = new ArrayList<Future<String>>();ExecutorService exec = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {futureList.add(exec.submit(new MyCallable(i)));}while (finishedFutureList.size()<5) {for (Future<String> future : futureList) {if (finishedFutureList.contains(future))continue;try {if (future.isDone()) {System.out.println(future.get(0,TimeUnit.SECONDS));finishedFutureList.add(future);}} catch (InterruptedException | ExecutionException | TimeoutException e) {e.printStackTrace();}}}}}

Output every second and print the completed task first

2015-08-24 10:12:08 4 -- haha-callable2015-08-24 10:12:09 3 -- haha-callable2015-08-24 10:12:10 2 -- haha-callable2015-08-24 10:12:11 1 -- haha-callable2015-08-24 10:12:12 0 -- haha-callable

The result of the first completed task can be obtained through polling. But it is troublesome. Another better way is to complete the Service (CompletetionService)

class MyCallable implements Callable<String> {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");private int id;public MyCallable(int id) {this.id = id;}@Overridepublic String call() {try {TimeUnit.SECONDS.sleep(5 - id);} catch (InterruptedException e) {e.printStackTrace();}return df.format(new Date()) + " " + id + " -- haha-callable";}}public class TestExecutor {public static void main(String[] args) {ExecutorService exec = Executors.newCachedThreadPool();CompletionService<String> completionService = new ExecutorCompletionService<String>(exec);for (int i = 0; i < 5; i++) {completionService.submit(new MyCallable(i));} for (int j = 0; j < 5; j++) {try {Future<String> f = completionService.take();System.out.println(f.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}}}
Output every second and print the completed task first

2015-08-24 10:29:53 4 -- haha-callable2015-08-24 10:29:54 3 -- haha-callable2015-08-24 10:29:55 2 -- haha-callable2015-08-24 10:29:56 1 -- haha-callable2015-08-24 10:29:57 0 -- haha-callable

CompletetionService extracts completed tasks through take and encapsulates them into Future.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.