Question

I wrote a game in Java with a client and server side using sockets and data input/output streams. The server side sometimes needs to send a message to all users in a "for" loop, but since writing to a socket can block I created a thread for each user that sends him messages (as well as another thread for each user that listens to incoming messages). The sending thread is built on this idea:

private ArrayList<Object> messages = new ArrayList<Object>(),
                          newMessages = new ArrayList<Object>();

public void run() {
    while (true) {
        for (Object message: messages) {
            try {
                if (message instanceof Byte)
                    out.writeByte((Byte)message);
                else if (message instanceof Boolean)
                    out.writeBoolean((Boolean)message);
                else if (message instanceof String)
                    out.writeUTF((String)message);
                else if (message instanceof Integer)
                    out.writeInt((Integer)message);
                else if (message instanceof Long)
                    out.writeLong((Long)message);
            } catch (IOException e) {}
        }
        synchronized (newMessages) {
            messages.clear();
            messages.addAll(newMessages);
            newMessages.clear();
        }
    }
}

public void write(Object message) {
    synchronized (newMessages) {
        newMessages.add(message);
    }
}

Unfortunately the run() method is constantly running, so I'd like to insert a sleep command, to achieve something like this:

private ArrayList<Object> messages = new ArrayList<Object>(),
                          newMessages = new ArrayList<Object>();

public void run() {
    while (true) {
        try {
            if (messages.isEmpty() && newMessages.isEmpty())
                sleep(0);
        } catch (InterruptedException e) {}
        for (Object message: messages) {
            try {
                if (message instanceof Byte)
                    out.writeByte((Byte)message);
                else if (message instanceof Boolean)
                    out.writeBoolean((Boolean)message);
                else if (message instanceof String)
                    out.writeUTF((String)message);
                else if (message instanceof Integer)
                    out.writeInt((Integer)message);
                else if (message instanceof Long)
                    out.writeLong((Long)message);
            } catch (IOException e) {}
        }
        synchronized (newMessages) {
            messages.clear();
            messages.addAll(newMessages);
            newMessages.clear();
        }
    }
}

public void write(Object message) {
    synchronized (newMessages) {
        newMessages.add(message);
        interrupt();
    }
}

But this may cause the thread to go to sleep when there are messages to send, for example when the write() method is called just after the run() method made the isEmpty() check, that returned true, but has not yet started to sleep. I really can't think of a way to avoid this problem with sleep(0), has anyone got a idea? Or am I going the wrong way about this?

Thanks a bunch.

Was it helpful?

Solution

Have a look at LinkedBlockingQueues. You could use a single one of those instead of the messages and newMessages objects in your code.

This class lets you add items from one thread A and read from another thread B. Thread B will wait until new messages are added by thread A. Should be exactly what you need.

OTHER TIPS

I would rethink your solution, using ScheduledExecutorService for executing a Runnable in background. This class also allows executing periodic actions at a fixed rate:

Your Runnable class:

public void run() {
    for (Object message: messages) {
        try {
            if (message instanceof Byte)
                out.writeByte((Byte)message);
            else if (message instanceof Boolean)
                out.writeBoolean((Boolean)message);
            else if (message instanceof String)
                out.writeUTF((String)message);
            else if (message instanceof Integer)
                out.writeInt((Integer)message);
            else if (message instanceof Long)
                out.writeLong((Long)message);
        } catch (IOException e) {}
    }
    synchronized (newMessages) {
        messages.clear();
        messages.addAll(newMessages);
        newMessages.clear();
    }

}

public void write(Object message) {
    synchronized (newMessages) {
        newMessages.add(message);
    }
}

Then, for invoking the Runnable:

  Runnable sender=new MyMessageSender(); //or whatever your class is called

  int threadPoolSize=1; //number of Threads you want to launch
  ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(threadPoolSize);

  final ScheduledFuture<?> senderHandle =
       scheduler.scheduleAtFixedRate(sender, 10, 10, SECONDS); //from now in 10 senconds, execute sender every 10 seconds
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top