AndroidUtilCode icon indicating copy to clipboard operation
AndroidUtilCode copied to clipboard

ThreadUtils内存泄露

Open haianhsing opened this issue 4 years ago • 3 comments

描述 Bug

调用ThreadUtils的executeAtFixedRate相关方法取消后会内存泄露,虽然task cancel了,但是timerTask没有cancel。

  • AndroidUtilCode 的版本:
  • 出现 Bug 的设备型号: Honor 20
  • 设备的 Android 版本: API 30

相关代码

ThreadUtils.executeByCachedAtFixRate(task, 1, 1, TimeUnit.SECONDS);
ThreadUtils.cancel(task);

异常堆栈

put the stack of crash here

截图

如果有的话请添加屏幕截图以帮助解释问题。 9fb3e212950aabc329c5b426d572830

haianhsing avatar Aug 20 '21 04:08 haianhsing

兄弟解决了吗,我也碰到了

thundeGG avatar Sep 14 '21 11:09 thundeGG

private static <T> void execute(final ExecutorService pool, final Task<T> task,
                                    long delay, final long period, final TimeUnit unit) {
        synchronized (TASK_POOL_MAP) {
            if (TASK_POOL_MAP.get(task) != null) {
                Log.e("ThreadUtils", "Task can only be executed once.");
                return;
            }
            TASK_POOL_MAP.put(task, pool);
        }
        if (period == 0) {
            if (delay == 0) {
                pool.execute(task);
            } else {
                TimerTask timerTask = new TimerTask() {
                    @Override
                    public void run() {
                        if (task.isCanceled()) {
                            cancel();
                            return;
                        }
                        pool.execute(task);
                    }
                };
                TIMER.schedule(timerTask, unit.toMillis(delay));
            }
        } else {
            task.setSchedule(true);
            TimerTask timerTask = new TimerTask() {
                @Override
                public void run() {
                    if (task.isCanceled()) {
                        cancel();
                        return;
                    }
                    pool.execute(task);
                }
            };
            TIMER.scheduleAtFixedRate(timerTask, unit.toMillis(delay), unit.toMillis(period));
        }
    }

@thundeGG 目前的做法是,执行前加了一个判断,如果task取消了,就不再执行timeTask

haianhsing avatar Sep 18 '21 08:09 haianhsing

private static <T> void execute(final ExecutorService pool, final Task<T> task,
                                    long delay, final long period, final TimeUnit unit) {
        synchronized (TASK_POOL_MAP) {
            if (TASK_POOL_MAP.get(task) != null) {
                Log.e("ThreadUtils", "Task can only be executed once.");
                return;
            }
            TASK_POOL_MAP.put(task, pool);
        }
        if (period == 0) {
            if (delay == 0) {
                pool.execute(task);
            } else {
                TimerTask timerTask = new TimerTask() {
                    @Override
                    public void run() {
                        if (task.isCanceled()) {
                            cancel();
                            return;
                        }
                        pool.execute(task);
                    }
                };
                TIMER.schedule(timerTask, unit.toMillis(delay));
            }
        } else {
            task.setSchedule(true);
            TimerTask timerTask = new TimerTask() {
                @Override
                public void run() {
                    if (task.isCanceled()) {
                        cancel();
                        return;
                    }
                    pool.execute(task);
                }
            };
            TIMER.scheduleAtFixedRate(timerTask, unit.toMillis(delay), unit.toMillis(period));
        }
    }

@thundeGG 目前的做法是,执行前加了一个判断,如果task取消了,就不再执行timeTask

这中写法虽然可以解决频繁向线程池提交任务,但是TIMER还是在一直执行,我觉得应该将TIMER的创建移动到execute中,并传递给Task,外部调用Task的cancel时将TIMER进行销毁

QjetAC901 avatar Mar 28 '25 08:03 QjetAC901