安全的线程利用率



对于长时间运行的线程,我使用单线程执行器:

executor = Executors.newSingleThreadExecutor(THREAD_FACTORY);
executor.submit(new LongRunnable());

检查要停止的标志:

private class LongRunnable implements Runnable {
        @Override
        public void run() {
            while (isRunning.get()) {
                try {
                    doSomething();
                } catch (InterruptedException e) {
                    ...
                }
            }
        }
    }

和整个执行以这种方式中断:

@Override
public void close() throws Exception {
    isRunning.set(false);
    executor.shutdownNow();
}

我仍然可以看到一些线程在profiler中没有gc-ed(而通过日志,他们正在执行的可运行线程已经退出最外层的while循环)。

问题:是否提供无内存泄漏和无线程泄漏的线程策略?

我无法看到executorshutDownNow的任何问题。可能你正在查看你的分析器中的不同线程。

尝试这个程序,它类似于你的问题,你可以看到线程在成功关闭后不再存在。

public class ExecutorShutdownTest {
private static ExecutorService executor;
private static AtomicLong executorThreadId = new AtomicLong(0);
public static void main(String[] args) {
    // get thread MX bean
    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    // create an executor and start the task
    executor = Executors.newSingleThreadExecutor(new TestThreadFactory());
    LongRunnable runnable = new LongRunnable();
    executor.submit(runnable);
    // main thread: keep running for sometime
    int count = 5;
    while (count-- > 0) {
        try {
            Thread.sleep(1000);
            System.out.println(String.valueOf(threadMXBean.getThreadInfo(executorThreadId.longValue())).replace("r", "").replace(
                    "n", ""));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    // main thread: stop the task
    try {
        runnable.close();
        System.out.println(String.valueOf(threadMXBean.getThreadInfo(executorThreadId.longValue())).replace("r", "").replace("n", ""));
    } catch (Exception e) {
        e.printStackTrace();
    }
    // main thread: run some more time to verify the executor thread no longer exists
    count = 5;
    while (count-- > 0) {
        try {
            Thread.sleep(1000);
            System.out.println(String.valueOf(threadMXBean.getThreadInfo(executorThreadId.longValue())).replace("r", "").replace("n", ""));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
private static class LongRunnable implements Runnable {
    private volatile boolean isRunning = true;
    @Override
    public void run() {
        while (isRunning) {
            System.out.println("Running");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //ignore
            }
        }
        System.out.println("Stopped");
    }
    public void close() throws Exception {
        System.out.println("Stopping");
        isRunning = false;
        executor.shutdownNow();
    }
}
private static class TestThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    TestThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
    }
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0) {
            @Override protected void finalize() throws Throwable {
                super.finalize();
                // probably bad idea but lets see if it gets here
                System.out.println("Executor thread removed from JVM");
            }
        };
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        executorThreadId.set(t.getId());
        System.out.println("Executor thread created");
        return t;
    }
}

}

下面是一个使用单线程执行器的示例程序,它设法将线程串在一起,以便JVM不能关闭,但它只能通过不调用shutdownNow:

来实现。
import java.util.concurrent.*;
public class Exec {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(new MyTask());
        Thread.sleep(20000L);
//        executor.shutdownNow();
        int retryCount = 4;
        while (!executor.isTerminated() && retryCount > 0) {
            System.out.println("waiting for tasks to terminate");
            Thread.sleep(500L);
            retryCount -= 1;
        }       
    }
}
class MyTask implements Runnable {
    public void run() {
        int count = 0;
        try {
            while (!Thread.currentThread().isInterrupted() && count < 10) {
                Thread.sleep(1000L);
                count += 1;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("all done");
    }
}

执行器使用的线程与任务有独立的生命周期,此示例显示了任务如何结束,而线程如何继续。取消对shutdownNow的注释将导致执行器线程终止。否则主线程会休眠一段时间并退出,使执行器的线程挂起,从而阻止JVM退出。

我猜你的close方法没有被调用,你的执行器从来没有被关闭。要获得更多有用的答案,请添加MVCE,以便我们可以重现问题。

考虑使用中断,不需要保留对Runnable的引用来设置标志。当我读到这个问题时,任务没有完成在这里不是一个问题,但它仍然会更好地使Runnable响应中断并失去标志,只是因为有更少的事情要跟踪总是一种改进。

最新更新