Error correction of "JAVA multi-threaded programming core technology" 3.1.10

The author of the book I read: Gao Hongyan

Book version: November 2015, 1st edition, 3rd printing

Problem overview:

3.1.10 Mainly explain the scene of waiting for the wait condition to change

For the sake of convenience, I will not copy the original code in the book. I use my own code is a main method I use jdk1.6

public static void main(String[] args) throws InterruptedException {
        final List<String> list = new ArrayList<String>();
        final Object lock = new Object();
// wait & delete
        Runnable waitRun = new Runnable() {
            @Override
            public void run() {
                synchronized( lock ){
                    try {
                        if( list.size() == 0 ){
                            System.out.println("wait begin t="+Thread.currentThread().getName());
                            lock.wait();
                            System.out.println("wait end"+Thread.currentThread().getName());
                        }
                        System.out.println("list remove begin"+Thread.currentThread().getName());
                        list.remove( 0 );
                        System.out.println("list remove end "+Thread.currentThread().getName() + " size="+list.size());
                    } catch (InterruptedException e) {
                        e.printStackTrace ();
                    }
                }
            }
        };
// wake up & increase
        Runnable notifyRun = new Runnable() {
            @Override
            public void run() {
                synchronized( lock ){
                    System.out.println("list add");
                    list.add( "1" );
                    System.out.println("notify begin");
                    lock.notifyAll();
                    System.out.println("notify end");
                }
            }
        };
        //Thread 1 - delete operation lock waiting
        Thread waitT1 = new Thread( waitRun );
        waitT1.start();
        //Thread 2 - delete operation lock waiting
        Thread waitT2 = new Thread( waitRun );
        waitT2.start();
        Thread.sleep( 1000L );
        //Thread 3 - Increment operation wakes up all waiting
        Thread notifyT1 = new Thread( notifyRun );
        notifyT1.start();
    }

There is a problem in this way, as in the book it is said that there will be a delete operation exception because there are no elements to delete.

Exception in thread "Thread-0" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

The solution given in the book is this

is to delete the thread while(list.size==0) This ensures that there are element to perform the delete operation.

// wait & delete
        Runnable waitRun = new Runnable() {
            @Override
            public void run() {
                synchronized( lock ){
                    try {
// Here is the modification point changed from the original if to while
                        while( list.size() == 0 ){
                            System.out.println("wait begin t="+Thread.currentThread().getName());
                            lock.wait();
                            System.out.println("wait end"+Thread.currentThread().getName());
                        }
                        System.out.println("list remove begin"+Thread.currentThread().getName());
                        list.remove( 0 );
                        System.out.println("list remove end "+Thread.currentThread().getName() + " size="+list.size());
                    } catch (InterruptedException e) {
                        e.printStackTrace ();
                    }
                }
            }
        };

This does ensure that the element is deleted only when there is an element, because when you have no element, the wait() operation will be looped all the time.

But there is a problem with the second deletion thread, because the collection has no elements to delete, there will be one more wait, and this wait() is generated in notifyAll and will never wake up.

So this approach belongs to solving old problems and creating new ones.

And my approach is this
Runnable waitRun = new Runnable() {
            @Override
            public void run() {
                synchronized( lock ){
                    try {
                        System.out.println("wait begin t="+Thread.currentThread().getName());
                        lock.wait();
                        System.out.println("wait end"+Thread.currentThread().getName());
// Whether the delete has been performed
                        boolean deleteFlag = false;
                        System.out.println( "list size="+list.size() +" remove before");
                        while( list.size() > 0 && deleteFlag == false ){
                            System.out.println("list remove begin"+Thread.currentThread().getName());
                            list.remove( 0 );
                            System.out.println("list remove end "+Thread.currentThread().getName() + " size="+list.size());
                            deleteFlag = true;
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace ();
                    }
                }
            }
        };

The idea is this, you can't move to the delete element in wait(), and the loop list.size > 0 && executes the deletion without executing the operation and can only execute it once, which solves this problem.

Summary :

When the wait condition changes, the basic idea is to use the while method to keep the thread waiting, and execute the next step when the condition is met.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326490887&siteId=291194637