1

我了解在多个线程之间访问共享实例变量是不安全的(除非该变量已声明volatile且正确synchronized)。我试图理解使用 Android 将共享实例变量传递给后台线程的语义AsyncTask

考虑以下代码:

public class Example {
    ContentValues contentValues;

    public void start() {
        contentValues = new ContentValues();
        contentValues.put("one", 1);
        new MyAsyncTask().execute(contentValues);
        contentValues.put("two", 2);
    }


    class MyAsyncTask extends AsyncTask<ContentValues, Void, Boolean> {
        @Override
        public void onPreExecute() {
            contentValues.put("three", 3);
        }

        @Override
        protected Boolean doInBackground(ContentValues... cvs) {
            ContentValues cv = cvs[0];
            return cv == contentValues; 
        }
    }
}

我们对局部变量cvin的状态了解doInBackground()多少?具体来说,

  • 保证其中包含哪些键值对。

  • 其中可能包含哪些键值对?

  • doInBackground()返回什么?

4

2 回答 2

1

如果您使用的是基本线程,则成员字段将不会同步,并且您提到的可见性将无法保证。

在使用 AsyncTask 的情况下,它取决于 AsyncTask 框架的实现。

"one", 1肯定会在那里,因为它是在创建线程之前放置的。

如果我们查看AsyncTask的源代码,我们可以找到以下注释:

* <h2>Memory observability</h2>
 * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
 * operations are safe without explicit synchronizations.</p>
 * <ul>
 *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
 *     in {@link #doInBackground}.
 *     <li>Set member fields in {@link #doInBackground}, and refer to them in
 *     {@link #onProgressUpdate} and {@link #onPostExecute}.
 * </ul>

所以"three", 3会在那里,因为它被添加到onPreExecute.

这也意味着该ContentValues contentValues;字段将在 点同步doInBackground,因此该方法将返回 true。

尽管我不认为该"two", 2项目保证存在,因为该代码与异步线程并行运行。可能在那里,但不一定。种族和知名度方面都会影响这一点。

于 2019-05-11T08:54:15.240 回答
1

哪些键值对保证在其中?其中可能包含哪些键值对?

  • 在 中onPreExecutecontentValues将只有一个值,即one=1。不会有two=2,因为你.put("two", 2)调用后执行。

  • doInBackgroundcontentValues必有three=3, two=2, one=1,因为你加two=2 and three=3在或之前onPreExecute

doInBackground() 会返回什么?

doInBackground背景将返回true,因为显然cv == contentValues(相同的实例)

作为对上述陈述的证明,我修改了您的Example类以在每个阶段打印一些消息,以便您了解实例变量的状态。

Example.kt

class Example {

    internal lateinit var contentValues: ContentValues

    fun start() {
        contentValues = ContentValues()
        contentValues.put("one", 1)
        MyAsyncTask().execute(contentValues)
        contentValues.put("two", 2)
    }


    internal inner class MyAsyncTask : AsyncTask<ContentValues, Void, Boolean>() {
        public override fun onPreExecute() {
            Log.d("TAG", "ContentValue in onPreExecute is $contentValues")
            contentValues.put("three", 3)
        }

        override fun doInBackground(vararg cvs: ContentValues): Boolean? {
            val cv = cvs[0]
            Log.d("TAG", "ContentValue in doInBackground is $contentValues")
            return cv == contentValues
        }

        override fun onPostExecute(result: Boolean?) {
            Log.d("TAG", "Result is $result")
            Log.d("TAG", "ContentValue in onPostExecute is $contentValues")
            super.onPostExecute(result)
        }
    }
}

输出

 ContentValue in onPreExecute is one=1
 ContentValue in doInBackground is three=3 two=2 one=1
 Result is true
 ContentValue in onPostExecute is three=3 two=2 one=1
于 2019-05-07T00:21:45.340 回答