Concurrent programming] [JUC extension assembly (Callable, FutureTask, Fork / Join framework, BlockingQueue)

Foreword

Before we have learned two ways to create threads, one is the direct successor Thread, another it is to implement Runnablethe interface.

These two methods have a defect: Unable to get the results after performing the task.

If you want to get a return value must be shared between variables by threads or means of communication, to implement more complex.

Therefore, Java5it provided the start Callableand Future, you can get the task execution results after the task is completed by them.

Callable

First we look at Runnablethe source code

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}
复制代码

It can be seen that runthe method returns a value ofvoid

The following look at Callablethe source code

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
复制代码

Can be seen, this is a generic interface, call()type returned by the function is passed in a V type.

CallableThe use of the code will be given later in the presentation.

Future

FutureClass located java.util.concurrentunder the package, which is an interface.

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;
}
复制代码

In the Futurestatement five interface methods, the following are sequentially explain the role of each method:

  • cancel method: to cancel the mission, if successful cancel the task is returned trueif you cancel the task failed to return false. Parameter mayInterruptIfRunningindicates whether to allow cancel the task being performed has not been completed, and if the task has been completed, the matter mayInterruptIfRunningis truestill false, this method definitely return false, that is, if the cancellation has been completed task returns false; if the task being performed, if mayInterruptIfRunningset true, returns trueIf the mayInterruptIfRunningset falseis returned false; if the task has not been executed, regardless of mayInterruptIfRunning为truewhat is sure to return true.

  • isCancelled method: Indicates whether the task was canceled successfully, if success is canceled before the task is completed normally is returned true.

  • isDone method: Indicates whether the task has been completed, if the task is completed, it returns true;

  • get () method: used to obtain the results, this method will produce obstruction, it will wait until the task is finished before returning;

  • get (long timeout, TimeUnit unit) used to obtain the results if the specified time, the results have not acquired, directly returns null.   

That Futureprovides three functions:

  • 1. The decision task is completed;

  • 2. can interrupt tasks;

  • 3. be able to get the task execution results.

Future use

@Slf4j
public class FutureExample {

    static class MyCallable implements Callable<String> {

        @Override
        public String call() throws Exception {
            log.info("do sth in callable");
            Thread.sleep(5000);
            return "done";
        }
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<String> future = executorService.submit(new MyCallable());
        log.info("do sth in main");
        Thread.sleep(1000);
        String result = future.get();
        log.info("result: {}", result);
    }
}
复制代码

operation result

FutureTask

FutureTaskLocated JUCwithin the bag but not the AQSsubclass.

FutureTaskImplements Futureand Runnableinterface, you can get the return value of the thread.

FutureTask use

@Slf4j
public class FutureTaskExample {


    public static void main(String[] args) throws InterruptedException, ExecutionException {

        FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info("do sth. in callable");
                Thread.sleep(5000);
                return "Done";
            }
        });

        new Thread(futureTask).start();
        log.info("do sth. in main");
        Thread.sleep(1000);
        String result = futureTask.get();
        log.info("result: {}", result);
    }
}
复制代码

Operating results the same as above.

As can be seen from the example of the use FutureTaskis very convenient when you want to use when starting a thread on it.

FutureTaskCan also pass multiple parameter types, we look into its source code

/**
 * Creates a {@code FutureTask} that will, upon running, execute the
 * given {@code Callable}.
 *
 * @param  callable the callable task
 * @throws NullPointerException if the callable is null
 */
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

/**
 * Creates a {@code FutureTask} that will, upon running, execute the
 * given {@code Runnable}, and arrange that {@code get} will return the
 * given result on successful completion.
 *
 * @param runnable the runnable task
 * @param result the result to return on successful completion. If
 * you don't need a particular result, consider using
 * constructions of the form:
 * {@code Future<?> f = new FutureTask<Void>(runnable, null)}
 * @throws NullPointerException if the runnable is null
 */
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}
复制代码

We can see FutureTaskcan pass 入Callableand Runnableincoming Runnablecan also specify the return value type.

Fork / Join framework

Fork/Join 框架It is Java7provided a frame for performing tasks in parallel, to obtain the final result by a large plurality of smaller tasks into smaller tasks final summary results of each task.

It is mainly used in 工作窃取算法this algorithm means a thread to steal from another task queue to execute the process shown below

Thread task uses a double-ended queue, you can only steal when stealing from the tail task. The advantage of this method is that you can take advantage of parallel computing threads, and reduces competition between threads. The disadvantage is that there is competition or, in extreme cases, such as when there is only one task queue.

Fork / Join framework has some limitations:

  • Tasks can only use the fork and join operations as a synchronization mechanism, if other synchronization mechanism, you can not perform other tasks while working thread.
  • IO operations task should not be performed
  • Tasks can not throw checked exceptions, they must be handled by the necessary code.

Coding demo

@Slf4j
public class ForkJoinTaskExample extends RecursiveTask<Integer> {

    public static final int threshold = 2;
    private int start;
    private int end;

    public ForkJoinTaskExample (int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        int sum = 0;

        //如果任务足够小就计算任务
        boolean canCompute = (end - start) <= threshold;
        if (canCompute) {
            for (int i = start; i <= end; i++) {
                sum += i;
            }
        } else {
            //如果任务大于阈值,就分裂成两个子任务计算
            int middle = (start + end) / 2;
            ForkJoinTaskExample leftTask = new ForkJoinTaskExample(start, middle);
            ForkJoinTaskExample rightTask = new ForkJoinTaskExample(middle + 1, end);

            //执行任务
            leftTask.fork();
            rightTask.fork();

            //任务结束后合并结果
            int leftResult = leftTask.join();
            int rightResult = rightTask.join();

            //合并子任务
            sum = leftResult + rightResult;
        }
        return sum;
    }

    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool();

        ForkJoinTaskExample task = new ForkJoinTaskExample(1, 100);

        Future<Integer> result = forkJoinPool.submit(task);

        try{
            log.info("result:{}", result.get());
        } catch (Exception e) {
            log.error("exception", e);
        }
    }
}
复制代码

Overridden computemethod is to call itself recursively continue the great task split into small tasks, the final summary of the results of the final result.

BlockingQueue

BlockingQueue that is blocking queue.

Enqueue operation when the queue is full and blocking operations will be the team the thread into the blocked state when blocking queue is empty.

The main scenario for producers and consumers.

ArrayBlockingQueue

Fixed size, the internal array implementation is a need to specify initialization capacity can not be changed.

FIFO stored data, the latest data in the tail.

DelayQueue

Obstruction of internal elements. The JUC DelayQueue element must implement the interface in the Delay (Delay interface inherits the Comparable interface), generally to the priority element expiration time of order.

Internal implementation is a priority queue and lock.

LinkedBlockingQueue

The size of the configuration options, specify the size is bounded initialization, if not specified is borderless.

Internal implementation is the list, other features consistent with ArrayBlockingQueue.

PriorityBlockingQueue

No borders, there are collation allows you to insert null.

All PriorityBlockingQueue insert objects must implement the Comparable interface.

SynchronousQueue

Synchronization Queue

Internal allows only accommodate one element, it will be blocked after internal insert an element, unless this element is consumed by other threads.

Written by Autu

2019.7.21

Guess you like

Origin juejin.im/post/5d33d1e05188252d1d5f9e09