"Java concurrent programming practice" the doubt of using newTaskFor to realize thread cancellation

problem description

in Chapter 7 of "Java concurrent programming practice", the author implements thread cancellation through newTaskFor method encapsulation. The demo given is as follows:

public abstract class SocketUsingTask <T> implements CancellableTask<T> {
    @GuardedBy("this") private Socket socket;

    protected synchronized void setSocket(Socket s) {
        socket = s;
    }

    public synchronized void cancel() {
        try {
            if (socket != null)
                socket.close();
        } catch (IOException ignored) {
        }
    }

    public RunnableFuture<T> newTask() {
        return new FutureTask<T>(this) {
            public boolean cancel(boolean mayInterruptIfRunning) {
                try {
                    SocketUsingTask.this.cancel();
                } finally {
                    return super.cancel(mayInterruptIfRunning);
                }
            }
        };
    }
}


interface CancellableTask <T> extends Callable<T> {
    void cancel();

    RunnableFuture<T> newTask();
}


@ThreadSafe
class CancellingExecutor extends ThreadPoolExecutor {
    public CancellingExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public CancellingExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public CancellingExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public CancellingExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        if (callable instanceof CancellableTask)
            return ((CancellableTask<T>) callable).newTask();
        else
            return super.newTaskFor(callable);
    }
}

I think this code is a little roundabout and not straightforward. I personally feel that it can be implemented in another way:

public abstract class SocketUsingTask<T> extends FutureTask<T> implements Callable<T> {
    @GuardedBy("this") private Socket socket;
    
    protected synchronized void setSocket(Socket s) { socket = s; }
    
    public synchronized void cancel() {
        try {
            if (socket != null)
                socket.close();
        } catch (IOException ignored) { }
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        try {
            cancel();
        } finally {
            return super.cancel(mayInterruptIfRunning);
        }
    }
}

I want to know what is the biggest advantage of this demo given in Java concurrent programming practice? Why do you want to do this? Because it feels like there are some superfluous operations. Thank you

May.20,2022

questions like this can be raised a lot, such as why ArrayList inherits the List interface, and directly inheriting the Collection interface can also directly implement
Software Engineering. There are some very important practical principles, such as the opening and closing principle, which determine the expansibility of your code. It is normal not to understand it at first, and it is all practical experience. The project can not meet the requirements at that time, but also need to consider the later expansion and maintenance


I think the writing of demo has two points:
the first demo re-extends Callable < T > with CancellableTask < T >. If you want to add new functions, you can achieve the effect of interface-oriented programming, and your direct implementation of extensibility is certainly not as good as this.
second, I think newTaskFor is the factory that generates RunnableFuture < T >. It seems that


is not used in this demo.

you should take a look at AbstractExecutorService's submit and the code for the entire inheritance chain

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

the above code submit is equivalent to calling CancellingExecutor's newTaskFor because your subclass is overridden. The demo in the book only needs to write a class that inherits SocketUsingTasks and overrides call, or an anonymous implementation. Then put it into CancellingExecutor for execution.

Menu