1

I am now learning about concurrency, and I tried to write a programm which should demonstrate a happens-before relationship when using concurrent collection. As stated in java.concurrent package:

The methods of all classes in java.util.concurrent and its subpackages extend these guarantees to higher-level synchronization. In particular: Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread.

I wrote the next class:

import java.util.Deque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;

public class HappensBefore {
    static int      checks      = 0;
    static int      shouldNotHappen   = 0;

    static Deque<Integer> syncList    = new LinkedBlockingDeque<>();
    public static boolean varToHappenBefore = false; //this var must be false when new element added to collection

    static AtomicBoolean  flag        = new AtomicBoolean();

    static class SyncTask implements Runnable {

    int       localTemp = -1;
    private final Thread t   = new Thread(new Counter());

    @Override
    public void run() {
        t.start();
        while (syncList.isEmpty()) { //just skip
        }

        while (true) {
        if (!Thread.interrupted()) {
            if (flag.get()) {
            int r = syncList.peekLast();
            if (r != localTemp) {
                if (varToHappenBefore) {
                shouldNotHappen++;
                }
                varToHappenBefore = true;
                localTemp = r;
                checks++;
            }
            flag.set(false);
            }
        } else {
            t.interrupt();
            break;
        }
        }
    }
    }

    static class Counter implements Runnable {
    int ctr = 0;

    @Override
    public void run() {
        while (!Thread.interrupted()) {

        if (!flag.get()) {
            flag.set(true);
            varToHappenBefore = false;
            syncList.add(ctr++);
        }
        }
    }
    }


    public static void main(String[] args) throws InterruptedException {
    SyncTask st = new SyncTask();
    Thread s = new Thread(st);
    s.start();
    Thread.sleep(10000);//runtime ms

    s.interrupt();
    // s1.interrupt();
    System.out.println("Elems times added: " + checks);
    System.out
        .println("Happens-before violated times: " + shouldNotHappen);
    }
}

What I make is launching thread1, which lauches thread2. Thread1 checks public boolean varToHappenBefore which is initially set to false. When thread1 saves new elem from the collection, it sets this boolean to true. On the next new elem. recieving if this boolean is still true, happens-before violation has occured, and shouldNotHappen is incremented.

Thread1 checks if concurrent collection has new element, if so, saves it in temp var and increments overall counter checks. Then it toggles atomic boolean for letting thread2 to add new element. Before adding new element, varToHappenBefore is set to false. Because of atomic boolean flag, thread2 doesn't run the code, before thread1 does. But toggling the flag in thread2 is done before adding element and checking varToHappenBefore, because these two operations(elem add and boolean toggle) are synchronized via atomic boolean otherwise. I ensure that thread2 runs only once after thread1 runs. And if varToHappenBefore happens before adding elem. to collection in thread2 which is then read in thread1(thread1 checks varToHappenBefore only if new elem is read from collection), then varToHappenBefore must remain 0 after program ends. But I get next results:

Elems times added: ~10 000 000

Happens-before violated times: 0-10

Probably I make something wrong, multithreading is subtle and complex. Hope for your help.

Edit: I need to escape the situation when thread1 setFlag(false) right after it is true and before getting the elem. from collection, because thread2 then can work between getting the element from collections and setting varToHappenBefore = true; in thread 1. If I make AtomicBoolean.compareAndSet() checking block, then I get 80k fails per 8mils. And it's predictible and clear. But when I make no such a window for thread2 to add additional elements between reading from collection and setting boolean, I still get few sequential reads, when boolean is true and new element appeared.

Natal
  • 154
  • 2
  • 10
  • It's really hard for me to follow what the code is attemping to do. One thing I noticed is that you're not using [`AtomicBoolean.compareAndSet()`](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html#compareAndSet%28boolean,%20boolean%29) for updating the flag. Instead you're using individual `get` and `set` calls, which defeats the purpose of using an atomic boolean in the first place because nothing prevents the value from changing between the calls. – Mick Mnemonic Sep 06 '15 at 23:05
  • Also, you have global `static` variables (e.g. `syncList` and `flag`) whose states should be `synchronized` somehow. Just because you're using an atomic boolean for storing one state, doesn't mean that you don't need to synchronize access to all interdependent variables. – Mick Mnemonic Sep 06 '15 at 23:15
  • Ok, you are probably right about AtomicBoolean.compareAndSet(). I made changes, result is the same. – Natal Sep 06 '15 at 23:27
  • Yes, that alone isn't enough. I think that you'd need to wrap the two blocks accessing `syncList` and `flag` (`if (flag.get()) {...` and `if (!flag.get()) {...`) in `synchronized` blocks, using a shared monitor, e.g. `private static final Object MONITOR = new Object();`. Then you can actually use a plain old `boolean` as `flag`. I don't see how the code would work without external synchronization. – Mick Mnemonic Sep 06 '15 at 23:42
  • [AtomicBoolean vs Synchronized block, whats the difference](http://stackoverflow.com/questions/28155245/atomicboolean-vs-synchronized-block-whats-the-difference) might clear things up a bit. – Mick Mnemonic Sep 06 '15 at 23:47
  • @MickMnemonic I'm not trying to make this code working. I understand the difference between synchronized block and AtomicBoolean. It works as it is. I'm trying to check concurrent collections happens-before constraint, and this test fails. I don't understand why it fails. `syncList` and `flag` are threadsafe. The list is threadsafe(concurrent collection) and needs no synchronization, the same as flag, which is atomic. Edited the question, more info there. – Natal Sep 07 '15 at 00:15

3 Answers3

3

Your example is little bit complex to analyze and fix. But if you are just doing this to understand the happens-before contract in case of concurrent collections then try this program -

public class HappensBeforeTest {

private static boolean producerStopped;

private static final Queue<String> queue = new LinkedBlockingQueue<>();
//private static final Queue<String> queue = new LinkedList<>();

public static void main(String[] args) {
    new Thread(new Producer()).start();
    new Thread(new Consumer()).start();
}

private static class Producer implements Runnable {

    public void run() {
        int count = 0;
        while (count++ < 10) {
            producerStopped = (count == 10);
            queue.add(String.valueOf(count));
        }
        System.out.println("Producer finished");
    }
}

private static class Consumer implements Runnable {

    public void run() {
        while (!producerStopped) {
            queue.poll();
        }
        System.out.println("Consumer finished");
    }
}

}

When queue is LinkedBlockingQueue both threads Producer and Consumer finish.

But when queue is LinkedList sometimes Consumerdoes not finish and keeps running because the producerStopped flag is still visible as false for Consumer even if Producer has updated it to true.

hemant1900
  • 1,226
  • 8
  • 9
1

Your SyncTask clears flag even if it finds list has not been changed yet. So, you may just miss list addition events. This makes your assumptions incorrect:

  1. Next round after missing event, SyncTask finds list changed, it assumes Counter has finished its task, checks(successfull) and rearms varToHappenBefore. But Counter actually adds element to the list after that.
  2. Next round SyncTask founds list changed again, it assumes Counter has finished its task, checks varToHappenBefore and finds it to be not cleared. This is just because Counter has not cleared it yet.

Just move flag.set(false); into if (r != localTemp) block, and everything will work.

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • One note: In the second (1) round Counter as I understand adds element to the list not _after_ but **before** `varToHappenBefore` rearms, because if it does it after then `varHappensBefore` should be set to false before SyncTask next time finds list changed. – Natal Sep 07 '15 at 21:47
  • I mean only element addition to be happens after, without note of `varToHappenBefore`. You are right that `Counter` should clear `varToHappenBefore` before `SyncTask` rearm it for round(2) to be possible. – Tsyvarev Sep 07 '15 at 21:54
  • If I put `flag.set(false);` after rearming `varToHappenBefore = true;` then everything is ok. Fails occure otherwise. Also if I use a standart collection, nothing changes. So happens-before is probably approched by `AtomicBoolean flag` setting and reading, not by concurrent collection. I suppose my design is too complex for representing collections happens-before relationship. :( – Natal Sep 07 '15 at 22:06
  • 1
    `Also if I use a standart collection, nothing changes.` - No, in case of standard collection `.add()` and `.peekLast()`, executed concurrently, may *corrupt* entire collection object. Happens-before here is actually enforced by the queue object. But the thing is that *any thread-safe* implementation of the queue will enforce that relation. You may examine pseudo-collection `{int val;}` which `.add(e)` method execute `val = e`, and `.peekLast` just `return val;`. This collection is not *thread-safe*, but cannot be corrupted. – Tsyvarev Sep 07 '15 at 22:46
  • Just out of curiosity, could you post the code that works? I get a consistent output of `Elems times added: 0 Happens-before violated times: 0` with the suggested changes. (Which is probably not the "correct" output) – Mick Mnemonic Sep 07 '15 at 23:11
  • Hmm, cpu-cache-related issues are always hard to reproduce. And it is very hardware-dependent. Unlike to non-atomicy issues of some operation, which can be exhibited relatively simply. But you have triggered bug in you original code, how does it come that you doesn't trigger `Concurrent modification` exception in standard collection? Is your code actually executed on **several** cores? – Tsyvarev Sep 07 '15 at 23:35
  • @MickMnemonic You get 0 because of `localtemp=0`, you just have to make it -1 and put `flag.set(false);` after `varToHappenBefore = true;`. – Natal Sep 07 '15 at 23:51
0

The cleanest demonstration would be very simple. Have thread A create an instance of some mutable class, update it, and put(...) it to a queue. Then, have thread B remove the instance from the queue, and verify that the state of the object is as expected.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57