2

I'm programming in java, and I have a List<LogEntry> logwhich is shared between different threads.

These "writers" threads are already synchronized between them, so only one thread at time can add or remove elements from log

However, because of the distributed algorithm that I'm trying to implement, there are portion of log which are "safe", which means that they cannot be modified neither by the writers or by the reader (which I introduce below) . This portion of log is indicated by the field int committedIndex, which is initialized to 0 and increase monotonically.

In conclusion, writers modify the elements in log in the range (commitIndex,log.size()), while there is a reader which get the elements in log contained in the range [0,commitIndex]. The reader starts to read from the first entry, then read the next one until he reaches log.get(commitIndex), then it stop and goes to sleep until commitIndex is increased. It updates a field lastApplied which is initialized to 0 and increases monotonically in order to remember the last logEntry that he read before going to sleep.

As you can see there is no need to synchronize the reader and the writers, since they access different portions of log.

My question is: how can I "wake up" the reader's thread when commitIndex is increased? I'd need something like this (executed by a writer):

if(commitIndex is updated)
{
     //wake up reader
}

And the reader:

public void run() {
    while(true){
        //go to sleeep...
        //now the reader is awaken!
        while(lastApplied<commitIndex){
            //do something with log.get(lastApplied)
            lastApplied++;
        }
    }

Obviously I terribly simplified my code in order to make you understand what I want as better as possible, I'm sorry if it's not clear enough (and don't hesitate to ask me anything about it). Thanks!

justHelloWorld
  • 6,478
  • 8
  • 58
  • 138
  • From the [apidocs](http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html): "If multiple threads access a linked/array list concurrently, and at least one of the threads modifies the list structurally, it _must_ be synchronized externally." Note the emphasis on _must_: your assumption that reading from a lower index while writing to a higher index is allright, is not valid. – vanOekel Mar 05 '15 at 00:21
  • The list can be modified structurally only in section which are not exploited by the Reader. So, there is NO sharing between Reader and Writers, but only between Writers (which are syncrhonized indeed) – justHelloWorld Mar 05 '15 at 17:28

2 Answers2

0

try this:

if(commitIndex is updated)
{
  //wake up reader
  synchronized(reader)
  {
    reader.notify();
  }
}
eebbesen
  • 5,070
  • 8
  • 48
  • 70
marchew
  • 21
  • 4
0

Use a shared LinkedBlockingQueue<Integer> (among the readers and all writers) to let every writer signal the reader that the commitIndex variable has been modified:

Writers:

if (commitIndex is updated) {
    // wake up reader
    this.queue.add(commitIndex);
}

Reader:

public void run() {
    while (true) {

        // take() puts this thread to sleep until a writer calls add()
        int commitIndex = this.queue.take();

        // now the reader is awaken!
        while (lastApplied < commitIndex) {
            // do something with log.get(lastApplied)
            lastApplied++;
        }
    }
}

Here I've used the attribute queue, which should correspond to the same instance of the LinkedBlockingQueue, for the reader and all writers.

Note: Exception handling left as an excercise.

fps
  • 33,623
  • 8
  • 55
  • 110
  • Your solution is not feasible for the problem for multiple reason: 1. the shared object is a list of `LogEntry` objects (as I stated at the beginning of the question), and not a list of int. So the (eventually) queue should contains `LogEntry` and not `Integer` objects. 2. I noticied that I didn't write that neither the reader can modify the "safe" (committed) part of the log, so using a queue (where the `take()` operation removes the head of the queue) is not allowed. In addiction I'd need a structure which implement random access operation (like a `List` indeed or an `Array`). BTW thanks :) – justHelloWorld Mar 04 '15 at 14:41
  • @justHelloWorld I'm not suggesting you should change your list of `LogEntry`, keep using that one. This queue of `Integer`s is intended just for signaling the readers. – fps Mar 04 '15 at 14:45
  • 1
    I'm sorry for my comment above, I didn't get your solution. I think your solution is perfectly correct, but I find the @marchew one more elegant since it doesn't introduce any new structure of support, unless you convince me otherwise. – justHelloWorld Mar 04 '15 at 14:49
  • @justHelloWorld The downside of @marchew's solution is that you need to use `synchronized` blocks to both wait and notify, as well as declaring `commitIndex` as `volatile`. With a queue, you wouldn't need anything like that, because it would handle synchronization. It would also let you transfer the `commitIndex` between threads in a straightforward manner. – fps Mar 04 '15 at 15:09
  • @justHelloWorld If you don't like the idea of introducing a new strucutre, you could use a [`ReentrantLock`](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html). This way, you'd avoid `synchronized` blocks, but would still need to declare `commitIndex` as `volatile` and pass it among threads. – fps Mar 04 '15 at 15:09