在android中这两个线程建立关系之前,是如何发生的



下面是自定义应用程序类和MainActivity类代码的示例代码:

public class App extends Application {
  private static String TAG = "APP";
  private int i;
  @Override
  public void onCreate() {
    super.onCreate();
    Log.d(TAG, Thread.currentThread().getName());
    HandlerThread t = new HandlerThread("init-thread");
    t.start();
    i = -100;
    Handler handler = new Handler(t.getLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            i = 100;
        }
    });
    handler.post(new Runnable() {
        @Override
        public void run() {
            MainActivity.MainHandler h = new MainActivity.MainHandler(Looper.getMainLooper(), App.this);
            h.sendEmptyMessage(0);
        }
    });
  }
  public int getI() {
    return i;
  }
}

以及主要活动类别:

public class MainActivity extends Activity {
  private static String TAG = "ACT-1";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
  @Override
  protected void onResume() {
    super.onResume();
    App app = (App) getApplication();
    Log.e(TAG, "i: " + app.getI()); //prints 100
  }
  public static class MainHandler extends Handler {
    private Application application;
    public MainHandler(Looper looper, Application app) {
        super(looper);
        this.application = app;
    }
    @Override
    public void handleMessage(Message msg) {
        App app = (App) application;
        Log.e(TAG, "MSG.what: " + msg.what);
        Log.e(TAG, "i: " + app.getI()); //prints 100
    }
  }
}

我想做的是在INIT-THREAD中将"I"的值更改为100以及从MAIN线程尝试读回该值。

我原以为onResume和handleMessage中的"I"值是-100,因为它们在MAIN线程中执行,但Log打印的值实际上是100。

在某种程度上,我试图重现普通java程序中每个人都会犯的经典错误,但android似乎能聪明地避免它

因此,我有兴趣了解android是如何实现两个线程之间先发生后发生的关系的。

在该程序中设置i的值时没有发生前关系。该程序包含一个数据竞赛,并且出现错误。

事实上,你已经看到它在几次测试中产生了一些特定的结果,这根本不能证明什么。虽然代码的行为是未定义的,但当你运行它时,它会做一些的事情。在任何特定的硬件上,它甚至可能在大多数时候都这样做。

快速查看代码,我没有看到任何读写重写,所以我认为使i易失性会使程序正确。然而,它不会使关于特定Log语句打印的值的任何注释断言都是准确的。

以下是规范之前发生的情况:

Java语言规范的第17章定义了内存操作(如共享变量的读取和写入)的先发生后发生关系。只有当写操作发生在读操作之前时,一个线程写操作的结果才保证对另一个线程读操作可见。

  1. synchronized和volatile构造,以及Thread.start()和Thread.join()方法,可以在关系。特别是:线程中的每个操作都发生在该线程中按程序顺序稍后出现的每个操作
  2. 监视器的解锁(同步块或方法退出)发生在每次后续锁定(同步块和方法)之前条目)。因为发生在关系之前是可传递的,线程在解锁之前的所有操作发生在任何线程锁定之后的所有操作之前监视器
  3. 对易失性字段的写入发生在对同一字段的每次后续读取之前。易失性字段的写入和读取类似内存一致性影响进入和退出监视器,但确实不需要互斥锁定
  4. 在已启动线程中的任何操作之前,都会调用线程上的启动
  5. 线程中的所有操作都发生在任何其他线程从该线程上的联接成功返回之前

参考:http://developer.android.com/reference/java/util/concurrent/package-summary.html

我在代码中评论解释:

public class App extends Application {
  private static String TAG = "APP";
  private int i;
  @Override
  public void onCreate() {
    super.onCreate();
    Log.d(TAG, Thread.currentThread().getName());
    HandlerThread t = new HandlerThread("init-thread");
    t.start();
    i = -100;
    Handler handler = new Handler(t.getLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            // before next line, i == -100
            // because if you look into handler.post,
            // it is using synchronized block to enqueue this Runnable.
            // And when this Runnable is dispatched,
            // it is using synchronized block of the same monitor.
            // So from 2. you can conclude the i = -100; happens-before here.
            i = 100;
        }
    });
    handler.post(new Runnable() {
        @Override
        public void run() {
            MainActivity.MainHandler h = new MainActivity.MainHandler(Looper.getMainLooper(), App.this);
            h.sendEmptyMessage(0);
        }
    });
  }
  public int getI() {
    return i;
  }
}

到目前为止,i的关系之前有发生。后来关系之前没有发生:

public class MainActivity extends Activity {
  private static String TAG = "ACT-1";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
  @Override
  protected void onResume() {
    super.onResume();
    App app = (App) getApplication();
    Log.e(TAG, "i: " + app.getI()); //prints 100
    // happens-before not guaranteed:
    // there is no happens-before operation between background thread that
    // sets i to 100 and main thread here is running.
  }
  public static class MainHandler extends Handler {
    private Application application;
    public MainHandler(Looper looper, Application app) {
        super(looper);
        this.application = app;
    }
    @Override
    public void handleMessage(Message msg) {
        App app = (App) application;
        Log.e(TAG, "MSG.what: " + msg.what);
        Log.e(TAG, "i: " + app.getI()); //prints 100
        // happens-before not guaranteed for the same reason
    }
  }
}

根据规范,正如Blake所说,最简单的解决方案是将i改为volatile。"然而,它不会使任何关于特定Log语句打印的值的注释断言都是准确的。"

您的代码之所以有效,是因为Handler#post方法强制执行的是发生在主线程和init-thread之间的关系之前。

如果您查看实现内部,MessageQueue#enqueueMessage在某个时刻具有一个使用self作为监视器的同步块。当MessageQueue(在其自己的线程中)读取并执行排队的消息/可运行消息时,将使用相同的监视器。

最新更新