如果有两个全局无变量int
变量a,b和object
obj;线程1具有:
1 ++a;
2 synchronized(obj){
3 ++b;
4 }
5 print a
Thead 2具有:
1 synchronized(obj){
2 ++b;
3 }
4 print a
所以我的问题是:
1,1号代码可以在线程1中的4行代码之后重新排序吗?
2,如果线程1首先锁定obj的锁,则线程2进行;因此线程2将在a
时正确获得print a
。
答案 0 :(得分:2)
Java语言规范中定义的以下规则适用于您的代码:
同步动作在动作上产生 synchronized-with 关系,定义如下:
- 监视器
m
上的解锁操作与m
上的所有后续 lock 操作(与“ ”同步),其中“后续”是根据同步顺序定义的。
<-->
两个动作可以通过 happens-before 关系进行排序。如果一个动作先于另一个,则第一个动作对第二个动作可见,并且在第二个动作之前
如果我们有两个动作
x
和y
,则我们写hb(x, y)
来表示x
发生之前y
如果
x
和y
是同一线程的动作,并且x
按程序顺序位于y
之前,则hb(x, y)
。 p>如果操作
x
与后续操作y
同步,那么我们也有hb(x, y)
。如果
hb(x, y)
和hb(y, z)
,则为hb(x, z)
。
现在,将其应用于您的代码:
A1 ++a;
A2 synchronized(obj){
A3 ++b;
A4 }
A5 print a
B1 synchronized(obj){
B2 ++b;
B3 }
B4 print a
规则2规定:
A1 -> A2 -> A3 -> A4 -> A5
B1 -> B2 -> B3 -> B4
结果取决于哪个线程首先到达synchronized
块。如果线程B首先到达,规则1 + 3指出:
B3 -> A2
组合:
A1 →→→→→→→→→→→→→ A2 → A3 → A4 → A5
↑
B1 → B2 → B3 →→→ B4
A1(++a
)和B4(print a
)之间没有先发生关系,因此结果是不可预测的。
如果线程A首先到达,则规则1 + 3指出:
B4 -> B1
组合
A1 → A2 → A3 → A4 →→→ A5
↓
→→ B1 → B2 → B3 → B4
规则4然后声明hb(A1, A5)
和hb(A1, B4)
,这意味着++a
被保证在两个print a
语句之前先出现。
要回答您的特定问题:
线程1中的4行代码之后,1行代码可以重新排序吗?
在线程1中?没有
从线程2可以看出吗?是的,请参见上文。
如果线程1首先锁定obj对象,则线程2这样做;这样,当打印a时,线程2将正确获得a。
是的,如果线程1首先锁定,则线程2将打印更新后的值a
,请参见上文。
答案 1 :(得分:1)
在同步块之前,之中或之后的语句无法跨这些边界重新排序。这些语句可以重新排序,它们也位于这些边界的同一侧。
如果这些字段都不是可变的,则所有投注均关闭。
这是没有同步的情况,但是,同步也会引入全部的内存障碍。
线程1中的4行代码之后,1行代码可以重新排序吗?
不能将其订购到第2行之后。
如果线程1首先锁定obj对象,则线程2这样做;这样,线程2在打印a时将正确获得a值。
提供的线程1在线程2之前进入synchronized
。但是,线程2可能首先执行,在这种情况下,它可能会看到旧值。
答案 2 :(得分:0)
请参见https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5上的“发生订单之前”部分。
在监视器上进行的每个后续锁定之前,发生监视器上的解锁。
这意味着,如果线程B获取了监视器(通过进入同步块),它将在释放监视器之前(即在同步块内和之前)对线程A所做的一切具有可见性。
对于问题1:打印的数字始终是递增值(程序顺序保留在线程A中)
对于问题2:线程B具有a
的更新值的可见性,因为它们是有序的:线程A更新a
>线程A释放监视器>线程B获取监视器>线程B读取a