发生在Java内存模型

时间:2015-09-10 03:25:08

标签: java multithreading concurrency java-memory-model

我有一些关于程序顺序以及它如何影响JMM中的重新排序。

在Java内存模型中,程序顺序( po )被定义为程序中每个线程中操作的总顺序。根据{{​​3}},这会导致发生之前( hb )边缘:

  

如果xy是同一个帖子的动作而x位于y之前   程序顺序,然后 hb(x,y)(即x发生在y之前。)

所以对于一个简单的程序P:

  initially, x = y = 0
     T1     |     T2
 -----------|-----------
  1. r1 = x | 3. r2 = y  
  2. y = 1  | 4. x = r2

我认为 po(1,2) po(3,4)。因此, hb(1,2) hb(3,4)

现在假设我想重新排序其中一些陈述,给我P':

  initially, x = y = 0
     T1     |     T2
 -----------|-----------
  2. y = 1  | 3. r2 = y  
  1. r1 = x | 4. x = r2

根据JLS,我们可以对任何两个相邻的语句(例如1和2)进行重新排序,前提是重新排序不会消除任何有效执行中的任何传递发生之前的边缘。但是,由于 hb (部分)由 po 定义,并且 po 是线程动作的总排序,似乎对我来说,如果不违反 hb 就不可能重新排序任何两个语句,因此 P' 不是合法的转换。

我的问题是:

  1. 我对 po hb 的理解是否正确,我是否正确地定义了 po hb 到上面的程序 P
  2. 我对 hb 失败的重新排序的理解在哪里?

3 个答案:

答案 0 :(得分:5)

你错过了JLS的这一部分:

  

应该注意的是,两个动作之间存在的先发生关系并不一定意味着它们必须在实现中以该顺序发生。如果重新排序产生的结果与合法执行一致,则不是非法的。

在您的情况下,由于1和2不相关,因此可以翻转它们。现在如果2是y = r1,那么1必须在2之前发生以获得正确的结果。

多处理器执行会出现真正的问题。没有任何先发生的边界,T2可能会在1之前观察到2,无论执行顺序如何。

这是因为CPU缓存。假设T1以任何顺序执行1和2。由于之前没有发生边界,因此这些操作仍然在CPU缓存中,并且根据其他需要,包含结果2的缓存部分可以在包含结果1的缓存部分之前刷新。

如果T2在这两个缓存刷新事件之间执行,它会发现2已发生且1没有发生,即2发生在1之前,只要T2知道。

如果不允许这样做,那么T1必须在1和2之间建立一个先发生的边界。

在Java中,有各种方法可以做到这一点。旧样式是将1和2放入单独的synchronized块中,因为synchronized块的开始和结束是发生在前的边界,即块之前发生的任何操作之前块,块内的任何动作都发生在阻塞之后的动作之前。

答案 1 :(得分:0)

你所描述的P'实际上不是一个不同的程序,而是同一个程序P的执行跟踪。它可能是一个不同的程序,但它会有不同的 po ,因此不同的 hb

发生前 - 关系限制关于其可观察效果的声明重新排序,而不是它们的执行顺序。操作 1 2 之前发生,但它们没有观察到彼此的结果,因此允许对它们进行重新排序。 hb 保证您将观察到两个动作按顺序执行,但仅来自同步上下文(即来自与 1 形成 hb 的其他动作和 2 )。您可以想到 1 2 说:让我们交换。没有人在看!

以下是JLS的一个很好的例子,反映了之前的想法:

  

例如,在线程构造的对象的每个字段中写入默认值不需要在该线程开始之前发生,只要没有读取就能观察到该事实

实际上,很少有可能在线程启动之前对线程构造的所有对象进行默认值写入,即使它们与该线程中的每个操作形成 synchronized-with 边缘。起始线程可能不知道它将在运行时构造什么,以及它将构造多少个对象。但是一旦你有一个对象的引用,你会发现默认值写入已经发生。排序尚未构造(或已知构造)的对象的默认写入通常不能反映在执行中,但它仍然不违反发生在之前的关系,因为它完全是关于可观察的效果。 / p>

答案 2 :(得分:0)

我认为关键问题在于你的构造P'。这意味着重新排序的方式是重新排序是全局的 - 整个程序以单一方式(在每次执行时)重新排序,它遵循内存模型。然后你试图推理这个P',并发现没有可能有趣的重新排序!

实际发生的是,与 hb 关系无关的语句没有特定的全局顺序,因此不同的线程可以在同一执行中看到不同的明显顺序。在您的示例中,一组中的{1,2}和{3,4}语句之间没有任何边可以按任何顺序查看另一组中的那些语句。例如,T2可能会在2之前观察1,然后T3T2import android.app.Activity; import android.app.ListActivity; import android.content.Intent; import android.support.v4.app.FragmentTransaction; import android.os.Bundle; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.util.SparseBooleanArray; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.support.v4.app.FragmentManager; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class ProductList extends AppCompatActivity { private List<myProductsView> myProducts_types = new ArrayList<myProductsView>(); ArrayAdapter<myProductsView> adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pop_productlist); //Button btnDel = (Button) findViewById(R.id.btnDel); //adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_multiple_choice,list); populateProductsList(); populateListView(); } private void populateProductsList() { myProducts_types.add(new myProductsView("aaa", "aaa", 1111, 12.90, R.drawable.cereal, 1)); myProducts_types.add(new myProductsView("aaa", "aaaaaa ", 1112, 10.90, R.drawable.cereal, 2)); myProducts_types.add(new myProductsView("aaa", "aaa", 1112, 30.00, R.drawable.cereal, 1)); myProducts_types.add(new myProductsView("aaa", "aaa", 1112, 20.00, R.drawable.cereal, 3)); } private void populateListView() { adapter = new MyListAdapter(); ListView list = (ListView) findViewById(R.id.product_list); list.setAdapter(adapter); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { System.out.println("BLALBALBLALBLABLAL"); Toast.makeText(ProductList.this, "BLA", Toast.LENGTH_SHORT); } }); } public void StartCalck(View view){ Intent intent = new Intent(ProductList.this, SplitBuying.class); startActivity(intent); } public class MyListAdapter extends ArrayAdapter<myProductsView>{ public MyListAdapter(){ super(ProductList.this, R.layout.pop_productlist, myProducts_types); } @Override public View getView(int position, View convertView, ViewGroup parent) { //make sure we have a view to work with(may have been given null View itemView = convertView; if(itemView == null){ itemView = getLayoutInflater().inflate(R.layout.product_item_view, parent, false); } //we need to populate the list //find the product to work with myProductsView currentProduct = myProducts_types.get(position); //fill the view CheckBox checkBox = (CheckBox)itemView.findViewById(R.id.product_checkBox); checkBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(ProductList.this,"BLA",Toast.LENGTH_SHORT); } }); TextView productname = (TextView) itemView.findViewById(R.id.product_name); productname.setText(currentProduct.getProductName()); EditText quantity = (EditText)itemView.findViewById(R.id.edit_text); quantity.setText(String.valueOf(currentProduct.getQuantity())); return itemView; } } } 相同(具有自己的私有变量) ,观察相反!所以没有单个重新排序P' - 每个线程可以观察它们自己的重新排序,只要它们与JMM规则一致。