[英]Why variable is not visible to other thread in synchronization?
Let's assume I've two threads t1
and t2
which are trying to access incX()
我们假设我有两个线程
t1
和t2
试图访问incX()
Here is my following code: 这是我的以下代码:
class Test implements Runnable {
private int x = 0;
public void incX() {
synchronized(this) {
x = ++x;
}
System.out.println("x is: "+x+" "+Thread.currentThread().getName());
}
public void run() {
incX();
}
public static void main(String[] args) {
Thread t1 = new Thread(new Test());
t1.start();
Thread t2 = new Thread(new Test());
t2.start();
}
Here's my output: 这是我的输出:
x is: 1 Thread-1
x is: 1 Thread-0
As in incX()
method I've synchronized x = ++x
, so the changes made to thread t1
should be visible to thread t2
, right? 在
incX()
方法中,我已经同步x = ++x
,因此对线程t1
所做的更改应该对线程t2
可见,对吧? So my output should be: 所以我的输出应该是:
x is: 1 Thread-1
x is: 2 Thread-0
I know ++x
is not an atomic operation but it is synchronized, so thread t2
can't acquire the lock. 我知道
++x
不是原子操作但它是同步的,因此线程t2
无法获取锁。 So the threads should not interleave
and changes made to x
should be visible to thread t2
, right? 所以线程不应该
interleave
,对x
更改应该对线程t2
可见,对吗? Am I misunderstanding? 我误会了吗? So my question is why I am not getting the output:
所以我的问题是为什么我没有得到输出:
x is: 2 Thread-0
You're using two separate instances of your Test
class, so naturally the x
in each of them only gets incremented once. 您正在使用
Test
类的两个独立实例,因此每个实例中的x
只会增加一次。 It's effectively the same as this: 它实际上与此相同:
Test test1 = new Test();
Test test2 = new Test();
test1.incX();
test2.incX();
Since each instance of Test
has its own x
, you'll see 1
twice with that code too. 由于每个实例
Test
都有自己的x
,你会看到1
与代码两次了。
To test synchronized access to the same instance, you need to use a single instance instead. 要测试对同一实例的同步访问,您需要使用单个实例。 For example:
例如:
class Test {
private int x = 0;
public void incX() {
synchronized(this) {
x = ++x; // See "Side Note" below
}
System.out.println("x is: "+x+" "+Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
Test test = new Test(); // One instance
Thread t1 = new Thread(() -> {
test.incX(); // Used by this thread
});
Thread t2 = new Thread(() -> {
test.incX(); // And also by this one
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
}
catch (Exception e) {
}
System.out.println("Done");
}
}
Which will output 哪个会输出
x is: 1 Thread-0 x is: 2 Thread-1 Done
or similar (naturally it varies depending on which thread gets scheduled when). 或类似的(当然,它取决于在什么时候安排的线程)。
Sometimes, it'll even look like this: 有时,它甚至看起来像这样:
x is: 2 Thread-0 x is: 2 Thread-1 Done
That's because the access to x
in the System.out.println
statement is outside the synchronized
block, so sometimes (not always) x
will get incremented after the end of the synchronized
block and before the println
: 这是因为在
System.out.println
语句中对x
的访问是在 synchronized
块之外 ,所以有时(并非总是) x
将在synchronized
块结束之后和println
之前递增:
synchronized(this) {
x = ++x;
}
// ***The other thread can jump in here and increment x
System.out.println("x is: "+x+" "+Thread.currentThread().getName());
In more detail: 更详细:
t1
enters the synchronized block t1
进入同步块 t2
tries to enter the synchronized block but has to wait because t1
has the lock t2
尝试进入同步块但必须等待,因为t1
具有锁定 t1
increments x
, making it 1 t1
递增x
,使其为1 t1
exits the synchronized block t1
退出同步块 t2
jumps in and increments x
, making it 2 t2
跳入并递增x
,使其为2 t2
exits the synchronized block t2
退出同步块 t1
outputs the current value of x
(2) t1
输出x
(2)的当前值 t2
outputs the current value of x
(2) t2
输出x
(2)的当前值 Note that Step 2 and Step 3 could be in any order, and Steps 6-8 could also be in any order. 请注意,步骤2和步骤3可以按任何顺序排列,步骤6-8也可以按任何顺序排列。
To reliably report x
as it was within the synchronized block after being incremented, we'd want to either: 为了在递增后可靠地报告在同步块内的
x
,我们想要:
Move the println
into the synchronized block 将
println
移动到synchronized块中
public void incX() { synchronized(this) { x = ++x; // See "Side Note" below System.out.println("x is: "+x+" "+Thread.currentThread().getName()); } }
or 要么
Save the result of the increment in a local variable 将增量的结果保存在局部变量中
public void incX() { int y; // Local variable synchronized(this) { y = ++x; // No longer and odd thing to do } System.out.println("x is: "+y+" "+Thread.currentThread().getName()); }
Unless you have a really, really good reason to hold a synchronization lock during output, go with #2. 除非你有一个非常好的理由在输出期间保持同步锁定,否则请使用#2。
Side note: As I mentioned in a comment, ++x
already writes its value back to x
, that's what the increment operators do. 旁注:正如我在评论中提到的,
++x
已经将其值写回x
,这就是增量运算符的作用。 So the x =
part of 所以
x =
部分
x = ++x;
...is unnecessary. ......是不必要的。 Either use the increment operator:
使用增量运算符:
++x;
...or don't: ......或者不:
x += 1;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.