Java alternately prints odd and even numbers. Two threads are blocked by wait.

the code is as follows:

import static java.lang.System.out;

/**
 * 
 *
 * @author ***
 * @create 2018-11-13 19:48
 */
public class Test0001 {
    public static Object OBJ = new Object();
    public static int i;

    public static class MyThread1 extends Thread {
        @Override
        public void run() {
            while( true ) {
                synchronized( OBJ ) {
                    out.println(Thread.currentThread().getName() + "  enter----  " + i);
                    if( i % 2 == 0 ) {
                        out.println(Thread.currentThread().getName() + "    " + iPP);
                        try {
                            out.println(Thread.currentThread().getName() + "    wait");
                            OBJ.wait();
                            out.println(Thread.currentThread().getName() + "    wait over");
                        } catch( InterruptedException e ) {
                            e.printStackTrace();
                        }
                    } else {
                        out.println(Thread.currentThread().getName() + "    notify");
                        OBJ.notify();
                    }
                    out.println(Thread.currentThread().getName() + "leave---      " + i);
                }
            }
        }
    }

    public static class MyThread2 extends Thread {
        @Override
        public void run() {

            while( true ) {
                synchronized( OBJ ) {
                    out.println(Thread.currentThread().getName() + "  enter----  " + i);
                    if( i % 2 == 1 ) {
                        out.println(Thread.currentThread().getName() + "    " + iPP);
                        out.println(Thread.currentThread().getName() + "    notify");
                        OBJ.notify();
                    } else {
                        try {
                            out.println(Thread.currentThread().getName() + "    wait");
                            OBJ.wait();
                            out.println(Thread.currentThread().getName() + "    wait over");
                        } catch( InterruptedException e ) {
                            e.printStackTrace();
                        }
                    }
                    out.println(Thread.currentThread().getName() + "leave---      " + i);
                }
            }
        }
    }

    public static void main( String[] args ) {
        Thread t1 = new MyThread1();
        Thread t2 = new MyThread2();
        t1.start();

        try {
            Thread.sleep(111);
        } catch( InterruptedException e ) {
            e.printStackTrace();
        }
        t2.start();
    }
}

the execution result is as follows:

Thread-0  enter----  0
Thread-0    0
Thread-0    wait
Thread-1  enter----  1
Thread-1    1
Thread-1    notify
Thread-1leave---      2    //1
Thread-1  enter----  2     //2
Thread-1    wait
Thread-0    wait over
Thread-0leave---      2
Thread-0  enter----  2
Thread-0    2
Thread-0    wait

then the program gets stuck
curiosity is why Thread-1 immediately enter (statement 2) after Thread-1 leave (statement 1), isn"t it reasonable to Thread-0 enter after Thread-1 leave? Ask for advice!


Let me give you an answer.

you can first make the following two changes to the Thread2 code, one of which is:

    public static class MyThread2 extends Thread {
        @Override
        public void run() {

            while( true ) {
                synchronized( OBJ ) {
                    out.println(Thread.currentThread().getName() + "  enter----  " + i);
                    if( i % 2 == 1 ) {
                        out.println(Thread.currentThread().getName() + "    " + iPP);
                        out.println(Thread.currentThread().getName() + "    notify");
                        OBJ.notify();
                    } else {
                        try {
                            out.println(Thread.currentThread().getName() + "    wait");
                            OBJ.wait();
                            out.println(Thread.currentThread().getName() + "    wait over");
                        } catch( InterruptedException e ) {
                            e.printStackTrace();
                        }
                    }
                    out.println(Thread.currentThread().getName() + "leave---      " + i);
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

the other is:

    public static class MyThread2 extends Thread {
        @Override
        public void run() {

            while( true ) {
                synchronized( OBJ ) {
                    out.println(Thread.currentThread().getName() + "  enter----  " + i);
                    if( i % 2 == 1 ) {
                        out.println(Thread.currentThread().getName() + "    " + iPP);
                        out.println(Thread.currentThread().getName() + "    notify");
                        OBJ.notify();
                    } else {
                        try {
                            out.println(Thread.currentThread().getName() + "    wait");
                            OBJ.wait();
                            out.println(Thread.currentThread().getName() + "    wait over");
                        } catch( InterruptedException e ) {
                            e.printStackTrace();
                        }
                    }
                    out.println(Thread.currentThread().getName() + "leave---      " + i);
                }
               Thread.yield();
            }
        }
    }

if you run the two changes separately, you will find that the first one can get the results you want.
in the second way, the answer is not necessarily , and it is very likely that two threads will be blocked in the end.

the running result of your code is not in line with your expectations, and you have mainly ignored two points:

  • CPU time slice
  • the lock is not acquired immediately after the wait is notify.

Java's object lock is implemented using the Monitor mechanism. The specific mechanism is as follows. You can also refer to an article " written by me :

.

when a thread needs to acquire an Object lock, it is put into the EntrySet to wait. If the thread acquires the lock, it becomes the owner of the current lock. If, according to program logic, a thread that has acquired the lock lacks some external conditions and cannot proceed (for example, the producer finds that the queue is full or the consumer finds that the queue is empty), then the thread can release the lock by calling the wait method, enter the wait set block and wait, and other threads have a chance to acquire the lock and do something else. As a result, the previously unestablished external condition is established, so that previously blocked threads can re-enter the EntrySet to compete for locks.

after your Thread2 adjusts the notify, Thread1 waits until Thread2 releases the lock, and it still has to stand on the same starting line as Thread2 to compete for the lock, so it is not absolutely said that after Thread2 releases the lock, it must be Thread1's turn to acquire the lock.

your code runs the same result every time, mainly because it is easier for Thread2 to regain the lock than Thread1, because Thread2's CPU time slice is most likely not used up.

the first change we made above is to let Thread2 sleep for a second, which definitely gives Thread1 plenty of time to acquire the lock, so the result will be as you expect.
the second modification is to get Thread2 to give up the CPU time slice, but again, when CPU reselects the thread to run, there is at least a 50-50 chance that Thread2 will still be selected.

above. I hope I can help you.


without looking at the specific logic, you probably check these two questions:
first: logical judgment do not use if, to check the specific reasons with while,.
second: if you ensure that notifyAll is set at every exit and do not use notify, to maintain fairness strategy, consider using explicit locks


.

refer to here:

https://github.com/crossoverJ.

Menu