1

So I'm trying to implement a multi-threading java code to simulate a Message server and client. they share a common resource to communicate. Client should only be able to read the message when server notifies it, after receiving it from the user. this continues until user types "quit".

now, the below code works JUST FINE:

import java.util.Scanner;

class Message{
    Scanner sc = new Scanner(System.in);
    private String text="";
    private boolean hasText;

    public void writeText(){
        while(hasText)
            try{
                wait();
            }catch (InterruptedException e){ System.out.println(e.toString()); }
        hasText = true;
        System.out.print("Server wants you to enter a message: ");
        text = sc.nextLine();
        System.out.println("Server wrote the message: " + text);
        notify();
    }

    public void readText(){
        while(!hasText)
            try{
                wait();
            }catch(InterruptedException e){System.out.println(e.toString());}
        System.out.println("Client read the current message: " + text);
        System.out.println("------------------------------");
        hasText = false;
        notify();
    }

    public String getText(){
        return text;
    }
}

class Server extends Thread{
    final Message m;
    Server(Message m){
        this.m = m;
    }
    public void run(){
        synchronized(m) {
            while (!m.getText().equals("quit"))
                m.writeText();
        }
    }
}

class Client extends Thread{
    final Message m;
    Client(Message m){
        this.m = m;
    }
    public void run(){
        synchronized (m) {
            while (!m.getText().equals("quit"))
                m.readText();
        }
    }
}

class B{
    public static void main(String[] args){
        Message m = new Message();
        new Server(m).start();
        new Client(m).start();
    }
}

Output:

Server wants you to enter a message: lol 
Server wrote the message: lol 
Client read the current message: lol 
------------------------------ 
Server wants you to enter a message: gg 
Server wrote the message: gg 
Client read the current message: gg 
------------------------------ 
Server wants you to enter a message: quit 
Server wrote the message: quit 
Client read the current message: quit 
------------------------------

but, when I try to implement the functions writeText() and readText() straight from the Server and Client class like this:

import java.util.Scanner;

class Message1{
    public String text="";
    public boolean hasText;
}

class Server1 extends Thread{
    Message1 m;
    Server1(Message1 m){
        this.m = m;
    }
    public void run(){
        synchronized(m) {
            while (!m.text.equals("quit")){
                while(m.hasText)
                    try{
                        wait();
                    }catch (InterruptedException e){ System.out.println(e.toString()); }
                m.hasText = true;
                System.out.print("Server wants you to enter a message: ");
                Scanner sc = new Scanner(System.in);
                m.text = sc.nextLine();
                System.out.println("Server wrote the message: " + m.text);
                notify();
            }
        }
    }
}

class Client1 extends Thread{
    Message1 m;
    Client1(Message1 m){
        this.m = m;
    }
    public void run(){
        synchronized (m) {
            while (!m.text.equals("quit")) {
                while(!m.hasText)
                    try{
                        wait();
                    }catch(InterruptedException e){System.out.println(e.toString());}
                System.out.println("Client read the current message: " + m.text);
                System.out.println("------------------------------");
                m.hasText = false;
                notify();
            }
        }
    }
}

class B1{
    public static void main(String[] args){
        Message1 m1 = new Message1();
        new Server1(m1).start();
        new Client1(m1).start();
    }
}

it raises the IllegalMonitorStateException.

and i desperately want to know why. Ugh. Appreciate the help :))

ps, i also changed the access modifiers of my message class' text and flag, to avoid the usage of getters and setters.

  • Your first version works because the thread that calls wait or notify always does so with the lock held on the object having wait or notify called on it. That isn’t true for the second version. – Nathan Hughes Apr 09 '21 at 14:56
  • The thing to look for is what lock is getting acquired. – Nathan Hughes Apr 09 '21 at 15:21
  • 1. Both Object.wait() and Object.notify() must be called inside a synchronized block. See their javadoc. And note, you should call both on the same object's monitor. 2. I'd strongly recommend using a blocking queue instead of the synchronized block. This will make your code much-much more clear and easy to understand. Take java.util.concurrent.SynchronousQueue for a single message channel or java.util.concurrent.LinkedBlockingQueue for a buffering message queue – AnatolyG Apr 12 '21 at 11:52

0 Answers0