简体   繁体   中英

Can synchronized statements on different objects interleave?

I am studying the keyword synchronized and I do not understand if it's possible to have 2 synchronized statements in the same class to interleave; example:

I made a class with these simple methods and two lock objects:

public class Locker1 {  
    private Object lock1= new Object();
    private Object lock2= new Object();

    public void printL1() {
        synchronized(lock1) {
            for(int i=0; i<50;i++) {
                System.out.println(i);
            }
        }
    }

    public void printL2() {
        synchronized(lock2) {
            for(int i=0; i<50;i++) {
                System.out.println("-"+i);
            }
        }
    }
}

I made two different threads that just call printL1 and printL2 , in my understanding I thought since these are two different locks I would see positive and negative numbers printed alternatively because concurrency is possible, but I tried many times and printL2 always happens entirely after printL1 , not once I saw a negative between positive numbers, am I missing something on synchronized?

Here is the rest of the code

public class ThreadA extends Thread {
    private Locker1 l1;

    public ThreadA(Locker1 l1) {
        this.l1=l1;
    }

    public void run() {
        l1.printL1();
    }
}
public class ThreadB extends Thread {   
    private Locker1 l1;

    public ThreadB(Locker1 l1) {
        this.l1=l1;
    }

    public void run() {
        l1.printL2();
    }   
}

and main class:

public class Main {
    public static void main(String[] args) {
        Locker1 l1=new Locker1();

        ThreadA tA=new ThreadA(l1);
        ThreadB tB=new ThreadB(l1);
        tA.start();
        tB.start();
    }
}

You understanding on Thread and Lock is correct. Since Locker1.printL1() and Locker1.printL2() grant the lock to different objects, One thread access Locker1.printL1() while another access Locker1.printL2() should not block each other.

The reason you see a complete printL2 result after printL1 is because the work is too little for the CPU. 50 iterations take a fraction of millisecond to complete. Before a context switch is needed, the task is completed.

Let the task sleep for a small amount of time and you can see how the thread yield.

public class Locker1 {
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void printL1() {
        synchronized (lock1) {
            for (int i = 0; i < 50; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    public void printL2() {
        synchronized (lock2) {
            for (int i = 0; i < 50; i++) {
                System.out.println("-" + i);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

output:

0
-0
1
-1
2
-2
3
-3
4
-4
5
-5
6
-6
7
-7
8
-8
9
-9
10
-10
11
-11
12
-12
13
-13
14
-14
15
-15
16
-16
17
-17
18
-18
19
-19
20
-20
21
-21
22
-22
-23
23
24
-24
25
-25
26
-26
27
-27
28
-28
29
-29
30
-30
31
-31
32
-32
33
-33
34
-34
35
-35
36
-36
37
-37
38
-38
39
-39
40
-40
41
-41
42
-42
43
-43
44
-44
45
-45
46
-46
47
-47
48
-48
49
-49

The interleaving happens as expected; it was hard to see with only 50 iterations, so putting in a lot more iterations shows it

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM