2014-10-04 47 views
0

下面是一个自定义的App类和MainActivity类别代码示例代码示例:怎么了之前发生关系建立在这两个线程的Android

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; 
    } 
} 

而且MainActivity类别:

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,并尝试从主线程读取值。

我期待的价值“i”的的onResume和的handleMessage是-100,因为它们是在主线程中执行,但日志打印值实际上100

是在某种程度上,我试图重现每个人在常规的java程序中都会犯的经典错误,但是android似乎明智地避免了它。

所以我有兴趣了解android如何实现两个线程之间的发生关系。

回答

0

在此程序中设置i的值时没有发生以前的关系。该程序包含数据竞争并且出错。

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

快速查看代码,我没有看到任何read-alter-rewrites,所以我认为使volatile变为i会使程序正确。然而,它不会对任何关于特定声明打印的价值的评论断言是准确的。

+0

实际上,由于Android的处理程序,实际上有一个发生之前的关系。阅读下面的答案。 – Petrakeas 2015-09-29 14:32:42

0

这里是之前发生规格:

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

  1. 同步的和挥发性的构建体,以及所述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; 
    } 
} 

直到现在还有之前发生关系的我。后来有没有之前发生关系:

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 
    } 
    } 
} 

,按照规范,同布莱克说,最简单的方法固定的比赛是我改变挥发。 “然而,它不会对任何关于特定Log语句打印的价值的评论断言是准确的。”

+0

几乎完全赞同@贺林的评论。处理程序是一种安全发布的方法,因此,如果onCreate中的“run”方法读取“i”的值,它将保证读取“-100”。然而,正如我所说的,在“run”方法中设置“i”与在“onResume”中读取之间的关系之前,没有发生任何事情。我需要花一点时间来完全确定读取“handleMessage” – 2015-09-30 14:38:40

+0

@Blake不应该'handler.post'确保'i = -100;'发生在它自己之前?我知道代码顺序没有反映执行顺序,但是没有同步事件(例如,locking,handler.post)确保在该行发生之前的代码 - 在该行之前? (我自己在这里不是很清楚)。 – 2015-09-30 20:31:53

0

您的代码工作原因是因为Handler#post方法强制主线程和init-thread之间发生之前的关系。

如果你看看执行内部,MessageQueue#enqueueMessage有一个同步块,使用self作为监视器。当MessageQueue(在它自己的线程中)读取并执行入队的消息/ runnables时使用相同的监视器。

相关问题