future task cancel (二)向下传递中断【重要】- (java object多大 java对象内存模型 数组有多长(十三))

1 测试用例

当future.get响应interruptedexception时,意味着外界想要中断你这个线程和所有子任务

那么应当在interruptedexception响应中对future进行cancel

 

 

 

 

 

 

 60 61 行一样结果

应当在38行处中断子任务writeTask

 

 

2 实例

 

 

 1)主线程被interrupt,135行,重置主线程旗标,并且cancel子线程

2)主线程cancel,同1)

3)子线程被interrupt,114行-127行-139行

4)子线程被cancel,141行

子线程

 

主线程

 

没有线程泄漏

 

testcase:

package markdown.memory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TestCancel {

    private static AtomicInteger no = new AtomicInteger(0);
    private static List<Boolean> assertRes = new ArrayList<>();
    private static Map<Integer, FutureTask> tasksLeak = new ConcurrentHashMap<>();
    private static Map<Integer, Thread> threadsLeak = new ConcurrentHashMap<>();
    private static BlockingQueue<Exception> queueException = new ArrayBlockingQueue<>(5);
    private static BlockingQueue<Boolean> queueIsInturruped = new ArrayBlockingQueue<>(5);
    private static int time = 2000;
    private static CountDownLatch assertLock = new CountDownLatch(1);
    public static void main(String []f) throws Exception {

        int no = registerSubTask();
        runMainTask(no);
        Thread.sleep(time);
        FutureTask cancelSubTask = tasksLeak.get(no);
        cancelSubTask.cancel(true);

        assertLock.await(time, TimeUnit.MILLISECONDS);
        assertRes.add(!tasksLeak.containsKey(no));
        assertRes.add(!threadsLeak.containsKey(no));
        Exception eMainTask = queueException.poll(time, TimeUnit.MILLISECONDS);
        assertRes.add(eMainTask.getCause() instanceof CancellationException);
        assertRes.add(eMainTask.getMessage().equalsIgnoreCase("sub task cancelled"));
        assertRes.add(queueIsInturruped.poll(time, TimeUnit.MILLISECONDS));

        assertLock = new CountDownLatch(1);
        no = registerSubTask();
        runMainTask(no);
        Thread.sleep(time);
        Thread interruptedThread = threadsLeak.get(no);
        interruptedThread.interrupt();

        assertLock.await(time, TimeUnit.MILLISECONDS);
        assertRes.add(!tasksLeak.containsKey(no));
        assertRes.add(!threadsLeak.containsKey(no));
        eMainTask = queueException.poll(time, TimeUnit.MILLISECONDS);
        assertRes.add(eMainTask.getCause() instanceof ExecutionException);
        assertRes.add(eMainTask.getMessage().equalsIgnoreCase("Error when domain size"));
        assertRes.add(!queueIsInturruped.poll(time, TimeUnit.MILLISECONDS));

        assertLock = new CountDownLatch(1);
        no = registerSubTask();
        Thread mainThread = runMainTask(no);
        Thread.sleep(time);
        mainThread.interrupt();

        assertLock.await(time, TimeUnit.MILLISECONDS);
        assertRes.add(!tasksLeak.containsKey(no));
        assertRes.add(!threadsLeak.containsKey(no));
        eMainTask = queueException.poll(time, TimeUnit.MILLISECONDS);
        assertRes.add(eMainTask.getCause() instanceof InterruptedException);
        assertRes.add(eMainTask.getMessage().equalsIgnoreCase("main task cancelled"));
        assertRes.add(queueIsInturruped.poll(time, TimeUnit.MILLISECONDS));

        assertLock = new CountDownLatch(1);
        no = registerSubTask();
        runMainTask(no);
        Thread.sleep(7000);

        assertLock.await(time, TimeUnit.MILLISECONDS);
        assertRes.add(!tasksLeak.containsKey(no));
        assertRes.add(!threadsLeak.containsKey(no));
        eMainTask = queueException.poll(time, TimeUnit.MILLISECONDS);
        assertRes.add(eMainTask.getCause() instanceof TimeoutException);
        assertRes.add(eMainTask.getMessage().equalsIgnoreCase("main task timeout"));
        assertRes.add(queueIsInturruped.poll(time, TimeUnit.MILLISECONDS));

        for(Boolean res : assertRes) {
            print(res);
        }
    }

    private static Thread runMainTask(int no) {
        Thread mainThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    runSubTask(no);
                } catch (Exception e) {
                    queueException.add(e);
                    print("status of main task " + Thread.currentThread().isInterrupted());
                    queueIsInturruped.add(Thread.currentThread().isInterrupted());
                }
            }
        });

        mainThread.start();
        return mainThread;
    }

    private static void runSubTask(int no) {

        FutureTask future = tasksLeak.get(no);
        Thread thread = new Thread(future);
        threadsLeak.put(no, thread);
        thread.start();

        try {
            future.get(4, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // 对主线程interrupt或canceL,进去这里
            print("status of main task after interrupted " + Thread.currentThread().isInterrupted());
            // 响应InterruptedException后重置,以在不抛错的情况下后续资源回收,表明主线程被人canceL了
            Thread.currentThread().interrupt();
            print("status of main task after interrupted 2 " + Thread.currentThread().isInterrupted());
            // 同时干掉子线程从
            future.cancel(true);
            throw new RuntimeException("main task cancelled", e);
        } catch (ExecutionException e) {
            // 子线程被interrupt,进入这里
            // 理论上应判别子线程是执行错误还是被人interrupt了,如果是后者,最后加上Thread.currentThread().interrupt(),因为子线程被人干掉了
            // 本例4个case中,只有这个case没有中断主线程
            throw new RuntimeException("Error when domain size", e);
        } catch (CancellationException e) {
            // 子线程被interrupt,进入这里
            // 同时干掉主线程
            Thread.currentThread().interrupt();
            throw new RuntimeException("sub task cancelled", e);
        } catch (TimeoutException e) {
            // 主线程超时
            print("status of main task after timeout " + Thread.currentThread().isInterrupted());
            // 上一句isInterrupted是false,无论是抛TimeoutException后重置角度还是从表明主线程放弃了的角度,都中断下主线程
            Thread.currentThread().interrupt();
            print("status of main task after timeout 2 " + Thread.currentThread().isInterrupted());
            // 同时干掉子线程
            future.cancel(true);
            throw new RuntimeException("main task timeout", e);
        } catch (Exception e) {
            throw new RuntimeException("unknown Error when domain size", e);
        }
    }

    private static int registerSubTask() {
        int num = no.incrementAndGet();
        FutureTask future = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                while(true) {
                    if (Thread.currentThread().isInterrupted()) {
                        tasksLeak.remove(num);
                        threadsLeak.remove(num);
                        assertLock.countDown();
                        throw new RuntimeException("sub task interrupted");
                    }
                }
            }
        });
        tasksLeak.put(num, future);
        return num;
    }

    private static void print(Object o) {
        System.out.println(o);
    }

}

 

posted on 2024-08-01 21:10  silyvin  阅读(20)  评论(0)    收藏  举报