【问题标题】:Can I rely on the garbage collector to stop an AsyncTask?我可以依靠垃圾收集器来停止 AsyncTask 吗?
【发布时间】:2012-08-18 12:25:07
【问题描述】:

一个快速的理论问题。假设我有一个 Java 类,它同时使用终结器和它自己的私有 AsyncTask 的实例,在其他任何地方都没有引用。

现在假设 AsyncTask 的 doInBackground 方法类似于:

while(go) {
 f();
}

而终结器是:

public void finalize() {
 go = false;
}

当对象的所有外部引用都被删除后,AsyncTask 会停止吗?或者系统会继续线程,永远不会删除对象,因为它被线程引用?

【问题讨论】:

    标签: java android garbage-collection


    【解决方案1】:

    我可以依靠垃圾收集器来停止 AsyncTask 吗?

    不,你不能。实际上,您可以依靠 GC NOT 停止任务。

    GC 只会终结一个无法访问的对象。但是根据定义,所有活动线程都是可访问的,这意味着AsyncTask 对象也将是可访问的。

    【讨论】:

      【解决方案2】:

      简短的回答:线程一直在运行,你不能依赖 GC 来阻止它。

      详细信息:我的问题无法得到足够的答案(不过,谢谢你,Alberto)我决定自己进行经验测试。使用以下测试代码:

      public class TestActivity extends Activity {
         private ThreadContainer mtc;
      
         public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
      
            setContentView(R.layout.main);
      
            mtc = new ThreadContainer();
         }
      
         public void btnFree_click(View view) {
            Log.v("TestActivity","Free clicked");
            mtc = null;
         }
      }
      
      public class ThreadContainer {
         private boolean go = true;
      
         public ThreadContainer() {
            new testThread().execute(1);
         }
      
         private class testThread extends AsyncTask<Integer,Integer,Integer> {
            protected Integer doInBackground(Integer... arg0) {
              while(go) {
                Log.v("testThread","I'm running...");
      
                try {
                  Thread.sleep(500);
                } catch (InterruptedException e) {
                  // do nothing
                }
             }
      
           return 0;
            }
        }
      
        @Override
        public void finalize() {
            go = false;
        }
      }
      

      我能够从 logcat 获得以下输出:

      I/ActivityManager(  244): Displayed com.example.test/.TestActivity: +509ms
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/TestActivity(13728): Free clicked
      D/dalvikvm(  512): GC_EXPLICIT freed 144K, 50% free 2891K/5767K, external 1685K/2133K, paused 164ms
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      D/dalvikvm(13449): GC_EXPLICIT freed 12K, 47% free 2894K/5379K, external 1685K/2133K, paused 94ms
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      V/testThread(13728): I'm running...
      

      如您所见,线程并没有停止,即使它的任务是完全私有的,并且不再有对其容器对象的任何外部引用。

      【讨论】:

        【解决方案3】:

        我认为 AsyncTask 永远不会停止,因为您正在这样做:go = false;,在while 循环之外。仅当循环内的go 条件更改为false 时,该循环才会停止。执行上面写的循环,go 条件永远不会更新,while 循环将只检查go 变量的初始状态。看看this SO question。

        一个右while循环的example

        class WhileDemo {
            public static void main(String[] args){
                int count = 1;
                while (count < 11) {
                    System.out.println("Count is: "
                                       + count);
                    count++;
                }
            }
        }
        

        这不会是一个无限循环,因为我们正在更改循环内的 while 条件。 也可以使用break语句正确退出循环。

        另一个考虑因素。如果AsyncTask 被标记为守护线程,当所有对该对象的外部引用都被删除时,系统应该停止该线程。有关守护线程herehere 的更多详细信息。

        【讨论】:

        • 我应该澄清一下。在这个例子中,go 是类的私有成员,它同时持有 AsyncTask 和终结器,因此 while 的每次迭代都引用要更改的变量由终结者。
        • 是的,我明白了。如果您以这种方式运行应用程序,您将有一个无限循环。然后,除非您在 while 循环中更改条件,否则 AsyncTask 将永远不会被 GC 标记为可终结。
        • 我会知道拒绝我的答案的原因。 @mimicocotopus 也证实了这一点。为什么?
        猜你喜欢
        • 2010-09-21
        • 2010-10-10
        • 2011-03-05
        • 2023-03-21
        • 1970-01-01
        • 1970-01-01
        • 2013-08-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多