[Reserved] Java programming logic (77) - asynchronous task execution services

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:

Copy the code
public interface ExecutorService extends Executor {
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    //... 其他方法
}
Copy the code

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:

Copy the code
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;
}
Copy the code

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:

  1. Completed normally, get method returns the results of its execution, if the task is a Runnable and did not provide results, return null
  2. Task execution throws an exception, get method will throw an exception packaging ExecutionException again, you can get the original abnormality by getCause unusual method
  3. 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:

Copy the code
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();
    }
}
Copy the code

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:

Copy the code
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;
}
Copy the code

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:

Copy the 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();
    }

}
Copy the code

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:

Copy the code
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) 
Copy the code

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:

Copy the code
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();
    }
}
Copy the code

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:

Copy the code
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}
Copy the code

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:

Copy the code
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
Copy the code

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:

Copy the 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);
    }
}
Copy the code

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:

Copy the code
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);
}
Copy the code

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:

Copy the 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);
}
Copy the code

Cancel the code for the method:

Copy the code
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;
} 
Copy the code

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.

Guess you like

Origin www.cnblogs.com/ivy-xu/p/12364862.html