Java and provides a framework contract, which greatly simplifies the development required to perform asynchronous tasks, in this section we'll explore this framework.
Before the presentation, the thread Thread represents both the tasks to be performed, also said that the implementation of the mechanism, and set up a framework for the introduction of the concept of "execution services", it will "mission submission" and "mission" phase separation "execution service" encapsulates the details of the execution of tasks, for job submission is concerned, it can focus on the task itself, such as submitting a job, get results, cancel the task, without the need for attention to detail to perform tasks, such as thread creation, task scheduling, thread closed and so on.
The above description may be more abstract, then, we step specifically addressed.
Basic Interface
First, let's look at the basic interface to perform tasks related to service:
- Runnable and Callable: for asynchronous tasks to be performed
- Executor and ExecutorService: that the implementation of service
- Future: The results are asynchronous tasks
Runnable和Callable
About Runnable and Callable, in the previous sections we have learned, we have said the task, Runnable does not return results, but there Callable, Runnable not throw an exception, and will Callable.
Executor和ExecutorService
Executor represents the simplest execution service, which is defined as:
public interface Executor { void execute(Runnable command); }
It is that you can execute a Runnable, returned no results. How the interface is not limited mandate, might create a new thread, a thread may be multiplexed thread pool, it may be executed in the caller's thread.
ExecutorService extends Executor, more services are defined, basic methods are:
public interface ExecutorService extends Executor { <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); //... 其他方法 }
These three have expressed submit submit a task, return type is the Future, upon return, but said the task was submitted, does not mean that has been executed, you can check the status of asynchronous tasks by Future, to obtain the final result, canceled tasks. We know, for Callable, the task ultimately have a return value, and for Runnable is no return value, and the second method can provide simultaneously submitted Runnable a result, returned at the end of asynchronous tasks, and for the third method, asynchronous the final task of the return value is null.
Future
We look at Future interface definition:
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; }
get the final result for the return of asynchronous tasks, if the task has not been executed, will block waiting for, get another method may block waiting for a limited time, if the timeout task is not finished, will throw TimeoutException.
cancel to cancel asynchronous tasks, if the task has been completed, or canceled, or for some reason can not cancel, cancel returns false, true otherwise. If the task has not yet begun, it is no longer running. But if the task is already running, you may not be able to cancel the parameter mayInterruptIfRunning said that if the task being performed, whether to call interrupt method interrupt thread, if false it will not, if true, will attempt to break the thread, but we from 69 Festival know, interrupt may not be able to cancel a thread.
isDone and isCancelled used to query task status. isCancelled indicates whether the task was canceled, just cancel method returns true, then the isCancelled method returns true, even if the mission of the threads have not really over. isDone indicates that the task is completed, for whatever reason are counted, may be normal end of the task, the task may be an exception is thrown, it could be the task was canceled.
Let us look at the get method, the final task about three results:
- Completed normally, get method returns the results of its execution, if the task is a Runnable and did not provide results, return null
- Task execution throws an exception, get method will throw an exception packaging ExecutionException again, you can get the original abnormality by getCause unusual method
- The task was canceled, get method throws an exception CancellationException
If you call the get method of the thread is interrupted, get method throws InterruptedException.
Future is an important concept is the key to achieve "submit task" and "mission" phase separation, is one of the "link", the task submitter and task execution services through its isolation respective point of interest, at the same time collaborate .
Basic Usage
A basic example
Having said that interface, and how to use it? We look at a simple example:
public class BasicDemo { static class Task implements Callable<Integer> { @Override public Integer call() throws Exception { int sleepSeconds = new Random().nextInt(1000); Thread.sleep(sleepSeconds); return sleepSeconds; } } public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> future = executor.submit(new Task()); // 模拟执行其他任务 Thread.sleep(100); try { System.out.println(future.get()); } catch (ExecutionException e) { e.printStackTrace(); } executor.shutdown(); } }
We use the factory class Executors created a task execution services, Executors have multiple static methods can be used to create ExecutorService, used here is:
public static ExecutorService newSingleThreadExecutor()
Indicate the use of a thread of execution for all services, follow-up we will detail Executors, note the difference with the Executor phase, which is singular, it is the interface.
No matter how ExecutorService is created for the user, usage are the same, submit an example of a task, after submitting, you can proceed to other things, then you can get the final result or abnormal processing tasks performed by Future.
Finally, we call ExecutorService the shutdown method, it will close the task execution services.
More ways of ExecutorService
Earlier we just introduced three ExecutorService submit method, in fact, it has the following methods:
public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; <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; }
There are two methods close, shutdown and shutdownNow, difference is, shutdown that is no longer accepting new tasks, but the task has been submitted will continue, even if the task has not yet started, shutdownNow not only do not accept the new assignment has been submitted but not yet executed the task will be terminated for the task being performed, the general calls the interrupt method of trying to break the thread, but the thread may not respond to interrupt, shutdownNow returns have been submitted but not yet performed the task list.
shutdown and shutdownNow does not block waiting, after they return does not mean that all tasks have been completed, but isShutdown method returns true. The caller can awaitTermination wait for the end of all tasks, it can be defined waiting time, if all tasks before the timeout is over, that returns true isTerminated method returns true, otherwise false.
ExecutorService There are two methods to submit a batch job, invokeAll and invokeAny, they have two versions, one of which defines waiting time.
invokeAll wait for all tasks are completed, the list of Future returned, isDone Future of each method returns true, but isDone is true does not mean that the task is executed successfully, may have been canceled, invokeAll can specify the wait time, if there is a timeout after the mission was complete, it will be canceled.
For invokeAny, as long as there is a task within the time limit successful return, and it will return the results of the task, other tasks will be canceled if no task successfully return within the time limit, throw TimeoutException, if all tasks are within the time limit over, but have undergone an exception thrown ExecutionException.
ExecutorService example of invokeAll
We introduce in Section 64 jsoup downloaded and analyzed by using HTML, we use it to see a invokeAll example, while two URL to download and analyze the title, the title of the content output, code:
public class InvokeAllDemo { static class UrlTitleParser implements Callable<String> { private String url; public UrlTitleParser(String url) { this.url = url; } @Override public String call() throws Exception { Document doc = Jsoup.connect(url).get(); Elements elements = doc.select("head title"); if (elements.size() > 0) { return elements.get(0).text(); } return null; } } public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(10); String url1 = "http://www.cnblogs.com/swiftma/p/5396551.html"; String url2 = "http://www.cnblogs.com/swiftma/p/5399315.html"; Collection<UrlTitleParser> tasks = Arrays.asList(new UrlTitleParser[] { new UrlTitleParser(url1), new UrlTitleParser(url2) }); try { List<Future<String>> results = executor.invokeAll(tasks, 10, TimeUnit.SECONDS); for (Future<String> result : results) { try { System.out.println(result.get()); } catch (ExecutionException e) { e.printStackTrace(); } } } catch (InterruptedException e) { e.printStackTrace(); } executor.shutdown(); } }
Here, another factory method Executors of newFixedThreadPool create a thread pool, so that multiple tasks can execute concurrently, on the thread pool, our next section.
Other code is relatively simple, we can not explain. Use ExecutorService, code written concurrent asynchronous tasks like writing sequential programs, the threads do not care about the creation and coordination, only need to submit the task, and the results can be, and greatly simplifies the development work.
The basic implementation principle
Learn the basic usage ExecutorService and Future, we look at their basic implementation principle.
ExecutorService main implementation class is ThreadPoolExecutor, it is based on the thread pool next section we will introduce the thread pool implementation. ExecutorService have an abstract implementation class AbstractExecutorService, this section, we briefly analyze its principle, and based on it to implement a simple ExecutorService, Future major implementation class is FutureTask, we will briefly discuss the principle.
AbstractExecutorService
Providing AbstractExecutorService submit, and invokeAny invokeAll default implementation, sub-class need only implement the following methods:
public void shutdown() public List<Runnable> shutdownNow() public boolean isShutdown() public boolean isTerminated() public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException public void execute(Runnable command)
In addition to execute, other methods and life-cycle management services related to the implementation, simplicity, we ignore its implementation, the main consideration execute.
submit / invokeAll / invokeAny will eventually call the execute, execute determines in the end how to perform tasks, simplicity, we create a thread for each task, a simple ExecutorService complete implementation class as follows:
public class SimpleExecutorService extends AbstractExecutorService { @Override public void shutdown() { } @Override public List<Runnable> shutdownNow() { return null; } @Override public boolean isShutdown() { return false; } @Override public boolean isTerminated() { return false; } @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return false; } @Override public void execute(Runnable command) { new Thread(command).start(); } }
For the previous example, the code can be created ExecutorService replaced with:
ExecutorService executor = new SimpleExecutorService();
You can achieve the same effect.
ExecutorService most basic method is to submit, how it is to achieve it? We look AbstractExecutorService code:
public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }
It calls newTaskFor generated a RunnableFuture, RunnableFuture is an interface, not only extends Runnable, but also extends the Future, a new method is not defined, as Runnable, it represents the tasks to be performed, passed to the execute method of execution, as the Future, it the results indicate asynchronous task execution. This may be confusing, we look at specific code:
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); }
The object is to create a FutureTask, FutureTask realized RunnableFuture interface. It is how to achieve it?
FutureTask
It has a member variable represents the task to be performed, declared:
private Callable<V> callable;
There integer variables state indicates the state, declared:
private volatile int state;
The value can be:
NEW = 0; // beginning of the state, or tasks running COMPLETING = 1; // temporary status, mission draws to a close, setting the result NORMAL = 2; // perform tasks normally completed EXCEPTIONAL = 3; // throw task execution abnormal end CANCELLED = 4; // task was canceled iNTERRUPTING = 5; // task is interrupted iNTERRUPTED = 6; // task is interrupted
There is a variable represents the final result of the execution or exception, declared:
private Object outcome;
There is a thread running variable represents the task:
private volatile Thread runner;
There is a one-way linked list represents a thread waits for the results of task execution:
private volatile WaitNode waiters;
FutureTask constructor initializes callable and state, if accepted FutureTask a Runnable object, it calls the Executors.callable to Callable object as follows:
public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
Task Execution Services will use the run method of a thread execution FutureTask, run () code:
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
The basic logic is:
- call method calls callable and catch any exceptions
- If properly executed, the call set result set, saved to the outcome
- If an exception execution occurs, call setException sets the exception, the exception is saved to the outcome, but the state is not the same
- In addition to setting and set setException result, modify the state, it will also call finishCompletion, it wakes up all the threads waiting for the results
For the task submitter, it gets results through the get method, the code limit the get method is as follows:
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (unit == null) throw new NullPointerException(); int s = state; if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING) throw new TimeoutException(); return report(s); }
The basic logic is that if the task is not yet finished, we wait, the final report calls reporting results, report or return results based on the state of exception is thrown, code:
private V report(int s) throws ExecutionException { Object x = outcome; if (s == NORMAL) return (V)x; if (s >= CANCELLED) throw new CancellationException(); throw new ExecutionException((Throwable)x); }
Cancel the code for the method:
public boolean cancel(boolean mayInterruptIfRunning) { if (state != NEW) return false; if (mayInterruptIfRunning) { if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING)) return false; Thread t = runner; if (t != null) t.interrupt(); UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state } else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED)) return false; finishCompletion(); return true; }
The basic logic:
- If the task is completed or canceled, returns false
- If mayInterruptIfRunning is true, calling interrupt interrupt threads, set the state INTERRUPTED
- MayInterruptIfRunning if false, the state is set CANCELLED
- FinishCompletion wake up call to all the threads waiting for the results
invokeAll和invokeAny
Understand FutureTask, we will look at other ways of AbstractExecutorService, invokeAll basic logic is very simple, for each task, create a FutureTask, and call execute to perform, and then wait for the end of all tasks.
invokeAny to achieve a little more complicated, it takes advantage of ExecutorCompletionService, about this class and realize invokeAny, we re-introduced in later chapters.
summary
This section describes the tasks performed in Java and contracting services basic concepts and principles, the service embodies the idea of concurrent asynchronous development "separation of concerns", the user only needs to submit tasks ExecutorService, by Future operating results of the task and can , you need not be concerned thread creation and coordination of details.
This section describes the basic principles AbstractExecutorService and FutureTask, to achieve a simple implementation services SimpleExecutorService, create a separate thread for each task. In practice, the implementation of the service most frequently used is based on the thread pool to achieve the ThreadPoolExecutor, concurrent programming thread pool is a very important concept and technology that allows us to explore the next section.