我们假设我有两个线程t1,t2它们正在尝试访问incX()
这是我的以下代码:
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();
}
Run Code Online (Sandbox Code Playgroud)
这是我的输出:
x is: 1 Thread-1
x is: 1 Thread-0
Run Code Online (Sandbox Code Playgroud)
正如incX()我已经同步的方法一样x = ++x,所以对线程所做的更改t1应该对线程可见t2,对吧?所以我的输出应该是:
x is: 1 Thread-1
x is: 2 Thread-0
Run Code Online (Sandbox Code Playgroud)
我知道++x这不是一个原子操作,但它是同步的,所以线程t2无法获取锁.所以线程不应该interleave和x线程可见的更改t2,对吧?我误会了吗?所以我的问题是为什么我没有得到输出:
x is: 2 Thread-0
Run Code Online (Sandbox Code Playgroud)
你正在使用你的类的两个单独的实例Test,所以x它们中的每一个实际上只增加一次.它实际上与此相同:
Test test1 = new Test();
Test test2 = new Test();
test1.incX();
test2.incX();
Run Code Online (Sandbox Code Playgroud)
由于每个实例Test都有自己的实例,因此x您也会看到1两次该代码.
要测试对同一实例的同步访问,您需要使用单个实例.例如:
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");
}
}
Run Code Online (Sandbox Code Playgroud)
哪个会输出
x is: 1 Thread-0 x is: 2 Thread-1 Done
或类似的(当然,它取决于在什么时候安排的线程).
有时,它甚至看起来像这样:
x is: 2 Thread-0 x is: 2 Thread-1 Done
这是因为访问x在System.out.println声明之外的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());
Run Code Online (Sandbox Code Playgroud)
更详细:
t1 进入同步块t2尝试进入同步块但必须等待,因为t1有锁t1增量x,使其成为1t1 退出同步块t2跳入并递增x,使其成为2t2 退出同步块t1输出当前值x(2)t2输出当前值x(2)请注意,步骤2和步骤3可以是任何顺序,步骤6-8也可以是任何顺序.
为了x在递增后可靠地报告在同步块内,我们想要:
移动println到同步块
public void incX() {
synchronized(this) {
x = ++x; // See "Side Note" below
System.out.println("x is: "+x+" "+Thread.currentThread().getName());
}
}
Run Code Online (Sandbox Code Playgroud)
要么
将增量的结果保存在局部变量中
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());
}
Run Code Online (Sandbox Code Playgroud)除非你有一个非常好的理由在输出期间保持同步锁定,否则请使用#2.
旁注:正如我在评论中提到的,++x 已经将其值写回x,这就是增量运算符的作用.所以x =部分
x = ++x;
Run Code Online (Sandbox Code Playgroud)
......是不必要的.使用增量运算符:
++x;
Run Code Online (Sandbox Code Playgroud)
......或者不:
x += 1;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
324 次 |
| 最近记录: |