Как ведут себя переменные экземпляра, передаваемые между потоками с помощью AsyncTask? - PullRequest
1 голос
/ 07 мая 2019

Я понимаю, что небезопасно получать доступ к переменной общего экземпляра между несколькими потоками (если переменная не объявлена ​​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; 
        }
    }
}

Что мы знаем о состоянии локальной переменной cv в doInBackground()?В частности,

  • Какие пары ключ-значение гарантированно находятся в нем.

  • Какие пары ключ-значение могут быть в нем?

  • Что вернет doInBackground()?

Ответы [ 2 ]

1 голос
/ 11 мая 2019

Если вы используете базовый поток, поле участника не будет синхронизировано, и видимость не будет гарантирована, как вы упомянули.

В случае использования 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 гарантированно будет там, поскольку этот код выполняется параллельно с асинхронным потоком. Может быть, но не обязательно. На это могут влиять как раса, так и аспекты видимости.

1 голос
/ 07 мая 2019

Какие пары ключ-значение гарантированно присутствуют в нем?Какие пары ключ-значение могут быть в нем?

  • в onPreExecute, contentValues будет иметь только одно значение, то есть one=1.Не будет two=2, потому что вы .put("two", 2) после вызова выполняете.

  • в doInBackground, contentValues будет иметь 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
...