无竞争条件:两个具有不同锁但共享数据相同的块
No race condition: Two blocks with different locks but same shared data
我有两个线程 thread_1 和 thread_2 在同一个对象上调用不同的方法 unsafeObj。
- thread_1 调用
unsafeObj.incrementVAR_v1()
10 次
- thread_2 调用
unsafeObj.incrementVAR_v2()
10 次
这两个实例方法都有一个 synchronized
块,带有不同的锁(LOCK_1 和 LOCK_2)访问相同的实例字段 VAR.
public void icrementVAR_v1() {
synchronized(LOCK_1) {
++VAR;
print("Thread 1: " + VAR)
}
}
public void incrementVAR_v2() {
synchronized(LOCK_2) {
++VAR;
print("Thread 2: " + VAR);
}
}
鉴于这两个 synchronized
块使用不同的锁,我预计 VAR
会被同时访问,从而导致更新丢失(VAR
小于 20)。然而,这不是我观察到的。有人可以向我解释为什么不是这样吗?
示例输出:
Thread 2: 2
Thread 1: 1
Thread 2: 3
Thread 1: 4
Thread 2: 5
Thread 1: 6
Thread 2: 7
Thread 1: 8
Thread 2: 9
Thread 1: 10
Thread 2: 11
Thread 1: 12
Thread 2: 13
Thread 1: 14
Thread 2: 15
Thread 1: 16
Thread 2: 17
Thread 1: 18
Thread 2: 19
Thread 1: 20
如我所料,字段VAR
上的并发访问(由于有两个不同的锁)确实导致竞争条件,但为了观察它一个需要大量的迭代(在我的例子中,每个线程有 100'000 次迭代)。经验教训:
- ☝ 竞争条件很难重现
- 尝试重现时使用大量迭代
10 只玩玩(当然不保证)
public class Main {
public static void main(String[] args) {
Thread t1=new Thread(()->{
for(int i=0;i<10;i++){
icrementVAR_v1();
}
}
);
Thread t2=new Thread(()->{
for(int i=0;i<10;i++){
incrementVAR_v2();
}
}
);
t1.start();
t2.start();
}
static Object LOCK_1=new Object();
static Object LOCK_2=new Object();
static int VAR=0;
public static void icrementVAR_v1() {
synchronized(LOCK_1) {
++VAR;
Thread.yield();
System.out.println("Thread 1: " + VAR);
}
}
public static void incrementVAR_v2() {
synchronized(LOCK_2) {
++VAR;
Thread.yield();
System.out.println("Thread 2: " + VAR);
}
}
public static void print(String s){
System.out.println(s);
}
}
示例输出:
Thread 1: 2
Thread 2: 2
Thread 1: 4
Thread 2: 5
Thread 1: 6
Thread 2: 7
Thread 1: 8
Thread 2: 9
Thread 1: 10
Thread 1: 11
Thread 2: 12
Thread 1: 13
Thread 2: 14
Thread 1: 15
Thread 2: 16
Thread 1: 17
Thread 2: 18
Thread 1: 19
Thread 2: 19
Thread 2: 20
我有两个线程 thread_1 和 thread_2 在同一个对象上调用不同的方法 unsafeObj。
- thread_1 调用
unsafeObj.incrementVAR_v1()
10 次 - thread_2 调用
unsafeObj.incrementVAR_v2()
10 次
这两个实例方法都有一个 synchronized
块,带有不同的锁(LOCK_1 和 LOCK_2)访问相同的实例字段 VAR.
public void icrementVAR_v1() {
synchronized(LOCK_1) {
++VAR;
print("Thread 1: " + VAR)
}
}
public void incrementVAR_v2() {
synchronized(LOCK_2) {
++VAR;
print("Thread 2: " + VAR);
}
}
鉴于这两个 synchronized
块使用不同的锁,我预计 VAR
会被同时访问,从而导致更新丢失(VAR
小于 20)。然而,这不是我观察到的。有人可以向我解释为什么不是这样吗?
示例输出:
Thread 2: 2
Thread 1: 1
Thread 2: 3
Thread 1: 4
Thread 2: 5
Thread 1: 6
Thread 2: 7
Thread 1: 8
Thread 2: 9
Thread 1: 10
Thread 2: 11
Thread 1: 12
Thread 2: 13
Thread 1: 14
Thread 2: 15
Thread 1: 16
Thread 2: 17
Thread 1: 18
Thread 2: 19
Thread 1: 20
如我所料,字段VAR
上的并发访问(由于有两个不同的锁)确实导致竞争条件,但为了观察它一个需要大量的迭代(在我的例子中,每个线程有 100'000 次迭代)。经验教训:
- ☝ 竞争条件很难重现
- 尝试重现时使用大量迭代
10 只玩玩(当然不保证)
public class Main {
public static void main(String[] args) {
Thread t1=new Thread(()->{
for(int i=0;i<10;i++){
icrementVAR_v1();
}
}
);
Thread t2=new Thread(()->{
for(int i=0;i<10;i++){
incrementVAR_v2();
}
}
);
t1.start();
t2.start();
}
static Object LOCK_1=new Object();
static Object LOCK_2=new Object();
static int VAR=0;
public static void icrementVAR_v1() {
synchronized(LOCK_1) {
++VAR;
Thread.yield();
System.out.println("Thread 1: " + VAR);
}
}
public static void incrementVAR_v2() {
synchronized(LOCK_2) {
++VAR;
Thread.yield();
System.out.println("Thread 2: " + VAR);
}
}
public static void print(String s){
System.out.println(s);
}
}
示例输出:
Thread 1: 2
Thread 2: 2
Thread 1: 4
Thread 2: 5
Thread 1: 6
Thread 2: 7
Thread 1: 8
Thread 2: 9
Thread 1: 10
Thread 1: 11
Thread 2: 12
Thread 1: 13
Thread 2: 14
Thread 1: 15
Thread 2: 16
Thread 1: 17
Thread 2: 18
Thread 1: 19
Thread 2: 19
Thread 2: 20