main 方法中的同步块

Synchronized block in the main method

在下面关于线程间同步的代码中,根据生成的输出,为什么控制被转移到新线程的执行,尽管在主线程中为同一个对象获取了锁 "dt"方法 ?

public class DemoThread extends Thread {

public DemoThread() {
}

public void run() {
    int i=0;
    synchronized(this) {
        while(++i<=5) {
            sum=i;
            try{
                sleep(1000);
                System.out.println("Woke up from sleep");
               if(i>=2) this.notify();
            }catch(InterruptedException ie) {
                ie.printStackTrace();
                System.exit(1);
            }
        }
    }
}
private static int sum;    

public static void main(String... args) {

    DemoThread dt = new DemoThread();
    dt.start();

    synchronized(dt) {
        try{
            System.out.println("main here");
            dt.wait();
            System.out.println("main here again");
            System.out.println("sum = " + sum);
        }catch(InterruptedException ie){
            ie.printStackTrace();
            System.exit(1);
        }
    }        
}
}

输出:

main here
Woke up from sleep
Woke up from sleep
Woke up from sleep
Woke up from sleep
Woke up from sleep
main here again
sum = 5

编辑:我想我能够找到一种可能的代码流程来解释输出:

1.Main线程进入main方法中的Sync块

2.call就等着发了。释放 dt 对象上的锁

3.New 线程进入 while 循环,因为它锁定了对象 dt

4.Thread.Sleep 被执行并且没有释放锁

5.notify 进行了调用但没有启动主线程(?)

6.New 主线程执行完毕

如有错误请指正

synchronized关键字不是用来控制一个线程的执行,它是用来保证在任何时候只有一个线程可以进入一段代码。 通常整个方法可以在 {} 之间同步或编码。

您还可以同步将在两个或多个线程之间共享的对象,通常是一些将由线程更新的数据结构,您需要确保状态一致且不会部分更新。

在您的示例中,同步没有争用,如果您扩展示例以引入一些对象和多个线程尝试写入和读取该对象,您将得到更好的理解。

你很接近:

1.Main thread enters in the Sync block in the main method.

2.call to the wait is made. Lock released on the dt object

3.New thread enters the while loop as it has the lock on the object dt

4.Thread.Sleep is executed and it doesn't release the lock

5.notify call is made but doesnot wake the main thread(?)

6.New and the main thread finish the execution

到第4步,都是正确的

这是第 5 步发生的情况:

调用

notify() 并通知 main() 线程。
但它现在没有机会再次 运行。
为什么 ?因为DemoThread线程没有释放锁。

notify()方法确实在synchronized语句中循环执行。

synchronized (this) {
    while (++i <= 5) {
        sum = i;
        try {
            sleep(1000);
            System.out.println("Woke up from sleep");
            if (i >= 2) {
                notify();
            }

        } catch (InterruptedException ie) {
            ie.printStackTrace();
            System.exit(1);
        }
    }

并且根据 Object.notify() javadoc :

The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.

因此 main() 线程可以 运行 只有当 run() 方法 DemoThread 被终止时。

要让 main() 线程再次进入 运行,您可以在 DemonThread run() 方法、synchronized 语句和 while声明。
你还应该让这个线程休眠一点,让 main() 线程再次进入 运行。

public void run() {
    int i = 0;

    while (++i <= 5) {
        // let a chance for other threads
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (this) {
            sum = i;
            try {
                sleep(1000);
                System.out.println("Woke up from sleep");
                if (i >= 2) {
                    notify();                   
                }

            } catch (InterruptedException ie) {
                ie.printStackTrace();
                System.exit(1);
            }
        }
    }
}

现在 i >= 2,和以前一样,其他线程会收到通知,但是当线程在 while 上循环时离开锁,然后休眠 100 毫秒,main() 线程可以 运行 再次.

这是输出:

main here

Woke up from sleep

Woke up from sleep

main here again

sum = 2

Woke up from sleep

Woke up from sleep

Woke up from sleep