Thread pool-stop the threads in the thread pool, correctly handle the exceptions of the threads in the thread pool

Java's own thread pool

Java provides a very useful tool class Executors, through Executors we can easily create a series of thread pools:

Executors.newCachedThreadPool, a thread pool of new threads can be created as needed. The thread created in the thread pool may be used to complete another task after completing a task.

Executors.newFixedThreadPool(int nThreads), to create a thread pool that can reuse a fixed number of threads. This thread pool contains at most nThread threads.

Executors.newSingleThreadExecutor(), creates an Executor using a single worker thread. Even if there are many tasks, only one thread is used to complete the task.

Executors.newSingleThreadScheduledExecutor(), creates a single-threaded execution program, which can be scheduled to run commands after a given delay or be executed periodically.

Friends who need more Java knowledge and interview questions can click the link below to get it for free

Link: 1103806531 Password: CSDN

Insert picture description here

If the thread submitted to the thread pool can be interrupted

The ExecutorService thread pool provides two convenient methods to stop the threads in the thread pool, they are shutdown and shutdownNow.

Shutdown will not accept new tasks, but will wait for the completion of existing tasks. And shutdownNow will try to immediately terminate existing running threads.

So how is it achieved? We look at an implementation in a ThreadPoolExecutor:

    public List<Runnable> shutdownNow() {
    
    
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
    
    
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
    
    
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

There is a call to the interruptWorkers() method, which is actually to interrupt the currently running thread.

So we can get a conclusion that the task submitted to ExecutorService must be interruptible, otherwise the shutdownNow method will fail.

    public void correctSubmit(){
    
    
        Runnable runnable= ()->{
    
    
            try(SocketChannel  sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080))) {
    
    
                ByteBuffer buf = ByteBuffer.allocate(1024);
                while(!Thread.interrupted()){
    
    
                    sc.read(buf);
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        };
        ExecutorService pool =  Executors.newFixedThreadPool(10);
        pool.submit(runnable);
        pool.shutdownNow();
    }

We need to add interrupt judgment in the while loop to control the execution of the program.

Correctly handle exceptions in threads in the thread pool

If an exception occurs in a thread in the thread pool, such as RuntimeException, how can we catch it? If the exception cannot be handled reasonably, unpredictable problems will occur.

Look at the following example:

    public void wrongSubmit() throws InterruptedException {
    
    
        ExecutorService pool = Executors.newFixedThreadPool(10);
        Runnable runnable= ()->{
    
    
            throw new NullPointerException();
        };
        pool.execute(runnable);
        Thread.sleep(5000);
        System.out.println("finished!");
    }

In the above example, we submit a task, and a NullPointerException will be thrown in the task. Because it is a non-checked exception, there is no need to explicitly capture it. After the task is completed, we basically cannot know whether the task has run successfully. Up.

So, how can we catch such thread pool exceptions? Here are a few methods for everyone.

The first method is to inherit ThreadPoolExecutor and rewrite

 protected void afterExecute(Runnable r, Throwable t) {
    
     }

with

protected void terminated() {
    
     }

These two methods.

Among them, afterExecute will be called after the task is executed. Throwable t saves possible runtime exceptions and errors. We can deal with it as needed.

And terminated is called after all tasks in the thread pool have been called. We can clean up some resources in it.

The second method is to use UncaughtExceptionHandler.

The Thread class provides a setUncaughtExceptionHandler method to handle the caught exceptions. We can add an UncaughtExceptionHandler to it when creating the Thread.

But ExecutorService executes one Runnable, how to use ExecutorService to submit Thread?

Don't be afraid, when Executors constructs the thread pool, we can also pass in the ThreadFactory to build a custom Thread.

    public void useExceptionHandler() throws InterruptedException {
    
    
        ThreadFactory factory =
                new ExceptionThreadFactory(new MyExceptionHandler());
        ExecutorService pool =
                Executors.newFixedThreadPool(10, factory);
        Runnable runnable= ()->{
    
    
            throw new NullPointerException();
        };
        pool.execute(runnable);
        Thread.sleep(5000);
        System.out.println("finished!");
    }

    public static class ExceptionThreadFactory implements ThreadFactory {
    
    
        private static final ThreadFactory defaultFactory =
                Executors.defaultThreadFactory();
        private final Thread.UncaughtExceptionHandler handler;

        public ExceptionThreadFactory(
                Thread.UncaughtExceptionHandler handler)
        {
    
    
            this.handler = handler;
        }

        @Override
        public Thread newThread(Runnable run) {
    
    
            Thread thread = defaultFactory.newThread(run);
            thread.setUncaughtExceptionHandler(handler);
            return thread;
        }
    }

    public static class MyExceptionHandler implements Thread.UncaughtExceptionHandler {
    
    
        @Override
        public void uncaughtException(Thread t, Throwable e) {
    
    

        }
    }

The above example is a bit complicated. Is there a simpler way?

some. In addition to execute to submit tasks, ExecutorService can also use submit to submit tasks. The difference is that submit will return a Future to save the results of the execution.

    public void useFuture() throws InterruptedException {
    
    
        ExecutorService pool = Executors.newFixedThreadPool(10);
        Runnable runnable= ()->{
    
    
            throw new NullPointerException();
        };
        Future future = pool.submit(runnable);
        try {
    
    
            future.get();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
    
            e.printStackTrace();
        }
        Thread.sleep(5000);
        System.out.println("finished!");
    }

When we call future.get() to get the result, the exception will also be encapsulated in ExecutionException, which we can get directly.

Pay attention to cleaning up when using ThreadLocal in the thread pool

We know that ThreadLocal is a local variable in Thread. If we use ThreadLocal during the running of the thread, when the thread is recycled and perform other tasks again, the previously set variables will be read, resulting in unknown problem.

The correct way to use it is to call the remove operation of ThreadLocal every time the thread finishes its task.

Or in the custom ThreadPoolExecutor, override the beforeExecute(Thread t, Runnable r) method and add the remove operation of ThreadLocal.

to sum up

I have compiled various knowledge points, modules, documents and more real interview questions from major companies. Friends in need can click the link below to get them for free

Link: 1103806531 Password: CSDN

Insert picture description here
Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_48655626/article/details/109250330