Java: Understand working Wait() notify() notifyAll()

Asked

Viewed 1,164 times

5

I’m studying Threads Java and its resources and I came across a question.

I have the following classes in my program:

public class Main {

    public static void main(String[] args) {

        ThreadB b = new ThreadB();
        b.start();

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

ThreadB:

public class ThreadB extends Thread {

    int total;

    @Override
    public void run() {
        synchronized (this) {

            for (int i = 0; i < 200; i++) {
                total += i;
            }

            notify(); //notifyAll();

            while(true) {
                //Do something
            }
        }
    }
}

Running this program I cannot get the expected behavior of the command notify() when I add the code snippet below in ThreadB:

 while(true) {
     //Do something
 }

See the exits with and without the quoted passage

Execução Threads

I’ll just have to wait it out ThreadB even Main notified that the total is already calculated?

There is something that can be done to get total without ThreadB finalize?

Before I got here I consulted this one doubt with similar subject matter from another user.

  • Really this is not necessary, I will edit. Thanks for the remark.

  • The example I mentioned is an abstraction of the project I am developing. Here and here . In the project I have Thread of socket clients whenever I accept a connection, and basically within the while of the thread, I have a socket manager. All I need to do is get some attributes from the thread created as soon as notify()

2 answers

6


The problem has nothing to do with wait or notify, but with its blocks synchronized.

See, the behavior of synchronized blocks is the guarantee that only one thread at a time is running for a given object.

Well, in this case the bow while(true) in your code makes your thread remain forever in the synchronized block, causing the notification sent to the thread main never really enter into force.

What you need to learn from all this is:

Synchronized blocks should contain as little code as possible

In other words, everything you run inside synchronized blocks, although it is thread-safe, blocks the rest of the program and can often become a bottleneck. Also, synchronized blocks can lead to deadlocks if poorly structured.

Summary:

Avoid synchronization whenever possible

3

Despite finding the answer sufficient, I found your question interesting and wanted to complement with a practical example - since you have practical needs, in addition to theoretical ones.

A great advantage of using the Java language is its vast standard API, so a piece of advice is to go to the competition documentation and find out what the Java API already provides: Docs.

I went ahead and made a working prototype of your code:

Main java.

public class Main {

    public static void main(String[] args) {
        Future<Integer> future = Executors.newSingleThreadExecutor().submit(new CallableB());

        int total = -1;

        try {
            System.out.println("Waiting for complete b...");
            total = future.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("Total: " + total);
    }
}

Callableb.java (a Threadb improvement)

public class CallableB implements Callable<Integer> {

    private int total = -1;

    public CallableB() {
        // executa o runnable
        Executors.newSingleThreadExecutor().execute(new RunnableB());
    }

    @Override
    public Integer call() throws Exception {
        if (total == -1) {
            synchronized (this) {
                wait();
            }
        }

        return mTotal;
    }

    private class RunnableB implements Runnable {

        @Override
        public void run() {
            int tmp = 0;

            for (int i = 0; i < 200; i++) {
                tmp += i;
            }

            total = tmp;

            synchronized (CallableB.this) {
                CallableB.this.notifyAll();
            }

            while(true) {
                //Do something
            }
        }
    }
}

In class Main, call the class CallabelB through Futures.

In class CallabelB implement the interface Callable that provides a Runnable with a return. In it I evoke the RunnableB which is the same implementation of ThreadB, however I use the artifice of the inner class that can access the members of the class where it is contained.

To manage the competition I use both your initial approach of the wait() and notifyAll(), on the verification of the variable modification total.

Like RunnableB does not have stop condition your program never ends, despite the last line of the method main be executed.

Then also take a look at Executorservice.

I hope I helped you.

Obs: CallableB.this is the mechanism to access the instance this class CallableB in RunnableB.

  • Interesting example, but have some reason to create two ExecutorServices? If you put the content of the method run in the method call can get rid of a good part of the code.

  • No, no reason. The ExecutorService class CallableB could be replaced by new RunnableB().run(). Already put the content of the method run in the call is not possible, because from what I understood this Runnable must continue to be executed - even after the calculation of total - and the method call awaiting a return.

  • Look here: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable.html.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.