Java threads: wait and notify methods

倖福魔咒の 提交于 2019-12-28 04:28:11

问题


I have a thread that calls the wait method and can only be awoken when the notify method called from some other class:

 class ThreadA {
     public static void main(String [] args) {
         ThreadB b = new ThreadB();
         b.start();

         synchronized(b) {
             try {
                 System.out.println("Waiting for b to complete...");
                 b.wait();
             } catch (InterruptedException e) {}
             System.out.println("Total is: " + b.total);
         }
     }
 }

class ThreadB extends Thread {
    int total;
    public void run() {
        synchronized(this) {
            for(int i=0;i<100;i++) {
                total += i;
            }
            notify();
        }
    }
}

In the above code if the synchronized block in main, if the ThreadA does not execute first and instead the other synchronization block executing and completes to completion, then ThreadA executes its synchronized block and calls wait, what is going to happen and how it will be notified again?


回答1:


If ThreadB gets through its synchronized block before ThreadA does, then ThreadA will block indefinitely on the call to wait. It won't somehow be notified that the other thread has already completed.

The problem is that you're trying to use wait and notify in ways that they are not designed to be used. Usually, wait and notify are used to have one thread wait until some condition is true, and then to have another thread signal that the condition may have become true. For example, they're often used as follows:

/* Producer */
synchronized (obj) {
    /* Make resource available. */
    obj.notify();
}

/* Consumer */
synchronized (obj) {
    while (/* resource not available */)
        obj.wait();

    /* Consume the resource. */
}

The reason that the above code works is that it doesn't matter which thread runs first. If the producer thread creates a resource and no one is waiting on obj, then when the consumer runs it will enter the while loop, notice that the resource has been produced, and then skip the call to wait. It can then consume the resource. If, on the other hand, the consumer runs first, it will notice in the while loop that the resource is not yet available and will wait for some other object to notify it. The other thread can then run, produce the resource, and notify the consumer thread that the resource is available. Once the original thread is awoken, it will notice that the condition of the loop is no longer true and will consume the resource.

More generally, Java suggests that you always call wait in a loop because of spurious notifications in which a thread can wake up from a call to wait without ever being notified of anything. Using the above pattern can prevent this.

In your particular instance, if you want to ensure that ThreadB has finished running before ThreadA executes, you may want to use Thread.join(), which explicitly blocks the calling thread until some other thread executes. More generally, you may want to look into some of the other synchronization primitives provided by Java, as they often are much easier to use than wait and notify.




回答2:


You could loop and wait until the total has been computed :

synchronized(b) {
   while (total == 0) {
       b.wait();
   }
}

You could also use a higher-level abstraction like a CountDownLatch.




回答3:


It is possible for ThreadB's run method to complete before you enter the synchronized block in ThreadA.main. In that situation, since the notify call has happened before you started waiting, ThreadA will block forever on the wait call.

A simple workaround would be to grab the lock on b in main before you start the second thread to ensure the wait happens first.

ThreadB b = new ThreadB();
synchronized(b) {
    b.start();
    ...
    b.wait();
}



回答4:


You probably want to use a java.util.concurrent.Semaphore for this.




回答5:


1) You need to add some flag that is used to communicate between the threads, so that B can signal to A when it is finished. A simple boolean variable is fine, as long as it is only read and written within the synchronized blocks.

synchronized(this) {
    for(int i=0;i<100;i++) {
        total += i;
    }
    isDone = true;
    notify();
}

2) A needs to loop while waiting. So if your boolean variable was called isDone, and was set to true by threadB, then threadA should have some code like this:

synchronized(b) {
    System.out.println("Waiting for b to complete...");
    while( ! isDone ) b.wait();
}

In this particular case, there's actually no reason to have the synchronized block in A - since threadB doesn't do anything after it finishes running, and A doesn't do anything except wait for B, threadA could simply call b.join() to block until it finishes. I assume that your actual use case is more complex than this.




回答6:


Why to make that complex ? Just use join() function of Thread.

ThreadB b = new ThreadB();
b.start();
b.join();
// now print b.total



回答7:


do not synchronized(thread), don't do it, do not synchronized(thread).. repat: no synchronized(thread) :)

And if you need to wait for the thread 'b' to finish, use b.join(), now your code is free to hang in b.wait()

--

Hopefully the source below can grant you an insight while sync(thread)/notify() I consider bad practice. (cut-cut)

Enjoy


To proceeed below you make sure you have accepted Oracle's License aggreement, found there: https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewLicense-Start?LicenseUUID=7HeJ_hCwhb4AAAEtmC8ADqmR&ProductUUID=pGqJ_hCwj_AAAAEtB8oADqmS&cnum=&evsref=&sln=

Java sources (incl), called in init(), effectively called by any java c-tor, since java 1.5

private static **synchronized int** nextThreadNum() {
return threadInitNumber++;
}

//join (the method w/ nanos only increase millis by one, if nanos>500000, millis==0 and nanos>0

public final **synchronized** void join(long millis) 
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;

if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
}

if (millis == 0) {
    while (isAlive()) {
    wait(0);
    }
} else {
    while (isAlive()) {
    long delay = millis - now;
    if (delay <= 0) {
        break;
    }
    wait(delay);
    now = System.currentTimeMillis() - base;
    }
}
}


public **synchronized** void start() {
    /**
 * This method is not invoked for the main method thread or "system"
 * group threads created/set up by the VM. Any new functionality added 
 * to this method in the future may have to also be added to the VM.
 *
 * A zero status value corresponds to state "NEW".
     */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    group.add(this);
    start0();
    if (stopBeforeStart) {
    stop0(throwableFromStop);
}
}

//stop1 is called after stop ensures proper priviledges

private final **synchronized** void stop1(Throwable th) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
    checkAccess();
    if ((this != Thread.currentThread()) ||
    (!(th instanceof ThreadDeath))) {
    security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
    }
}
    // A zero status value corresponds to "NEW"
if (threadStatus != 0) {
    resume(); // Wake up thread if it was suspended; no-op otherwise
    stop0(th);
} else {

        // Must do the null arg check that the VM would do with stop0
    if (th == null) {
    throw new NullPointerException();
    }

        // Remember this stop attempt for if/when start is used
    stopBeforeStart = true;
    throwableFromStop = th;
    }
}


来源:https://stackoverflow.com/questions/5121173/java-threads-wait-and-notify-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!