使用 AsyncTask 在线程之间传递的实例变量的行为是什么?
What is the behavior of instance variables passed between threads with AsyncTask?
我知道在多个线程之间访问共享实例变量是不安全的(除非变量声明为 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;
}
}
}
我们对 doInBackground()
中局部变量 cv
的状态了解多少?具体来说,
保证里面有哪些键值对
其中可能包含哪些键值对?
doInBackground()
return会怎样?
Which key-value pairs are guaranteed to be in it ? Which key-value pairs might be in it ?
在onPreExecute
中,contentValues
将只有一个值,即one=1
。不会有two=2
,因为你调用execute后.put("two", 2)
。
在 doInBackground
中,contentValues
将具有 three=3, two=2, one=1
,因为您在 onPreExecute
或之前添加了 two=2 and three=3
。
What will doInBackground() return?
doInBackground
后台会returntrue
,因为显然cv == contentValues
(同一个实例)
作为对上述陈述的证明,我修改了你的Example
class 以在每个阶段打印一些消息,以便你可以了解实例变量的状态。
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
如果您使用的是基本线程,则成员字段将不会同步,并且无法像您提到的那样保证可见性。
如果使用 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
点同步,因此该方法将 return 为真。
虽然我不认为 "two", 2
项一定会存在,因为该代码 运行 与异步线程并行。可能存在,但不一定。种族和能见度方面都会影响这一点。
我知道在多个线程之间访问共享实例变量是不安全的(除非变量声明为 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;
}
}
}
我们对 doInBackground()
中局部变量 cv
的状态了解多少?具体来说,
保证里面有哪些键值对
其中可能包含哪些键值对?
doInBackground()
return会怎样?
Which key-value pairs are guaranteed to be in it ? Which key-value pairs might be in it ?
在
onPreExecute
中,contentValues
将只有一个值,即one=1
。不会有two=2
,因为你调用execute后.put("two", 2)
。在
doInBackground
中,contentValues
将具有three=3, two=2, one=1
,因为您在onPreExecute
或之前添加了two=2 and three=3
。
What will doInBackground() return?
doInBackground
后台会returntrue
,因为显然cv == contentValues
(同一个实例)
作为对上述陈述的证明,我修改了你的Example
class 以在每个阶段打印一些消息,以便你可以了解实例变量的状态。
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
如果您使用的是基本线程,则成员字段将不会同步,并且无法像您提到的那样保证可见性。
如果使用 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
点同步,因此该方法将 return 为真。
虽然我不认为 "two", 2
项一定会存在,因为该代码 运行 与异步线程并行。可能存在,但不一定。种族和能见度方面都会影响这一点。