Runnable是否可以中断

背景:楼主在项目开发中遇到一个问题,在Activity中需要起一个子线程执行耗时任务,Activity退出onDestroy后,释放资源。刚好子线程执行耗时任务的时候需要访问被释放的资源,进而发生了空指针异常的崩溃。

原因:主线程和子线程是并发的,主线程Activity何时退出释放资源,子线程何时访问资源,两者时序不可控。

1)加锁可以吗?

主线程Activity退出 释放资源时候加锁,;子线程执行Runnable时加锁,并且子线程执行任务之前判断是否已经释放资源。

这样可能会导致ANR问题:万一子线程耗时任务很久,主线程退出Activity时由于锁被子线程持有一直等待,有ANR风险。

2) 不加锁,使用volatile关键字标记是否释放变量

主线程退出Activity时,volatile变量 设置为true代表已经释放。子线程的runnable 执行之前判断volatile变量的值,如果为true代表资源是否,不再执行。

上线后依旧有零星崩溃上报:子线程的runnable判断volatile变量时候,资源还没释放。执行后续的逻辑,资源释放了造成崩溃。

时序: 子线程判断volatile变量,通过——主线程释放资源——子线程后续的逻辑访问资源

于是楼主想到,主线程退出Activity时,应该释放生命周期内使用的资源,包括子线程的Runnable,让Runnable不执行或者中断。——但是真的可以中断吗?

  • 有人说Handler的removeCallbacksAndMessages——对于未执行的Runnable,可以,但是对于已经跑起来的Runnable, 不行。

  • 有人说Thread.interrupt, 但是interrupt仅仅起到通知被停止线程的作用,实际上对于被停止的线程而言,它拥有完全的自主权,它既可以选择立即停止,也可以选择一段时间后停止,也可以选择压根不停止。——这个是Java 不提供强制停止线程机制。

  • 有人说线程池的shutdownNow方法,但是shtdownNow方法本质也还是给正在执行的线程发送interrupt。

  • 有人说使用线程池的Submit(Runnable runnable) 返回一个Future,   最后通过Future的cancel方法不就可以了??——楼主亲测,跑起来的Runnable 还是不行。

以上尝试,使用如下Demo验证, Activity Destroy后,Runnable依旧执行......

public class TestActivity extends Activity {

    private Runnable runnable;
    private HandlerThread handlerThread;
    private Handler handler;
    private Thread thread;
    private Future future;
    private ExecutorService executorService;
    private Handler mainHandler = new Handler(Looper.getMainLooper());
    public static void startActivity(Context context) {
        Intent intent = new Intent(context, TestActivity.class);
        context.startActivity(intent);
    }
    private void init() {
        runnable = new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try{
                        Thread.sleep(1000);
                        Log.e("testing", "runnable run...." + TestActivity.this);
                    } catch (Exception e) {
                        Log.e("testing", "e run...." + e.toString());
                    }

                }
            }
        };
        //1. handlerThread
//        handlerThread = new HandlerThread("test-handler-thread");
//        handlerThread.start();
//        handler = new Handler(handlerThread.getLooper());
//        handler.post(runnable);

        //2. Thread
//        thread = new Thread(runnable);
//        thread.start();

        //3. future
        executorService = Executors.newSingleThreadExecutor();
        future = executorService.submit(runnable);
    }

    private void release() {
        //1.handlerThread
//        handlerThread.quit();
//        handler.removeCallbacks(runnable);

        //2. Thread
//        try {
//            thread.interrupt();
//        } catch (Exception e) {

//        }
        //3. future
        if(future != null) {
            boolean ret = future.cancel(true);
            executorService.shutdown();
            Log.e("testing", "future cancel...." + ret);
        }

    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        Log.e("testing", "act onCreate ");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        init();
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TestActivity.this.finish();
            }
        });
    }

    @Override
    protected void onDestroy() {
        release();
        Log.e("testing", "act onDestroy ");
        super.onDestroy();
    }
}

实际上,JAVA 推荐了使用Thread.currentThread().isInterrupted()的方法判断线程是否中断

也就是Demo中Runnable的 while(true) 修改为while(!Thread.currentThread().isInterrupted())

就Demo而言,可以实现中断Runnable,  但是实际项目的Runnable并不是一直循环,大多是一个耗费时间的任务。 Thread.currentThread().isInterrupted()判断不能一直贯穿整个Runnable, 只能在某一个时刻调用——万一在Runnable里调用isInterrupted()的时候线程还没interrupt,  调用isInterrupted()判断过后线程才interrupt, runnable仍然访问被释放的资源。

猜你喜欢

转载自blog.csdn.net/xiaobaaidaba123/article/details/125585284