[concurrency-interest] Is it a good idea to try manipulating theway JVM reorders?

Enno Shioji eshioji at gmail.com
Sat Dec 19 07:10:52 EST 2009


I understand. And I see you are also one of the authors of JCP!
Man, you guys keep helping me.

Thanks again for your time and for the book.
Have a good night!


Regards,
Enno



On Sat, Dec 19, 2009 at 8:44 PM, Joe Bowbeer <joe.bowbeer at gmail.com> wrote:
> A read-only HashMap is OK if, for example, it is created before any of the
> tasks are scheduled for execution.
>
> The necessary conditions for Safe Publication are covered in Section 3.5:
>
>   http://javaconcurrencyinpractice.com/
>
> Some flavors of read-only HashMap, such as an access-ordered LinkedHashMap,
> would not be OK...
>
> --Joe
>
> On Sat, Dec 19, 2009 at 3:24 AM, Enno Shioji wrote:
>>
>> Thanks again for the clarification! I think now I see the picture.
>>
>> > 3. One possible weakness in all these solutions is the HashMap.
>> >  Consider
>> > using a ConcurrentHashMap.
>> I was intending to use an unmodifiable HashMap that will be populated
>> once at object creation, and will not be modified afterwards, like
>> this:
>>
>> class Sample {
>>   private final Map<Id, AtomicReference> map;
>>
>>   public Task(){
>>      this.map = Collections.unmodifiableMap(getPopulatedMap());
>>   }
>>
>>   private static Map<Id, AtomicReference> getPopulatedMap(){
>>       //instantiate HashMap and populate it with (Id, AtomicReference)
>> pairs
>>   }
>> }//class
>>
>> This is okay, right?
>>
>>
>> Regards,
>> Enno
>>
>>
>>
>>
>> On Sat, Dec 19, 2009 at 7:51 PM, Joe Bowbeer wrote:
>> > To clarify further, I hope:
>> >
>> > 1. The Atomic* methods such as decrementAndGet perform synchronization
>> > actions, which do impose an inter-thread ordering.  The same goes for
>> > Phaser's methods, such as advance().
>> >
>> > 2. Phaser is suitable for this class of problem, and I think would
>> > result in
>> > more readable code.
>> >
>> > 3. One possible weakness in all these solutions is the HashMap.
>> > Consider
>> > using a ConcurrentHashMap.
>> >
>> > --Joe
>> >
>> > On Sat, Dec 19, 2009 at 2:32 AM, David Holmes wrote:
>> >>
>> >> > Maybe I'm not understanding the Java Memory Model correctly, but for
>> >> > example, is there really a difference between methodA and methodB
>> >> > below?
>> >> >
>> >> > class Sample {
>> >> >      AtomicReference<Boolean> ref = new AtomicReference<Boolean>();
>> >> >      Phaser phaser = getPhaser();
>> >> >
>> >> >      methodA(){
>> >> >         ref.set(Boolean.TRUE);
>> >> >         phaser.arrive();
>> >> >      }
>> >> >
>> >> >      methodB(){
>> >> >          phaser.arrive();
>> >> >          ref.set(Boolean.TRUE);
>> >> >      }
>> >> > }//class
>> >> >
>> >> > I wonder because, according to Java Concurrency in Practice:
>> >> > "There is no guarantee that operations in one thread will be
>> >> > performed
>> >> > in the order given by the program, as long as the reordering is not
>> >> > detectable from within that thread even if the reordering is apparent
>> >> > to other threads.[1]"
>> >> >
>> >> > Doesn't this mean that the compiler is free to reorder
>> >> > "phaser.arrive();" and "ref.set(Boolean.TRUE);" because indeed, that
>> >> > reordering is not detectable within that thread, while it is apparent
>> >> > to other threads? Thus, I thought you need to do something to prevent
>> >> > that.
>> >>
>> >> You _have_ done something to prevent that: you've used a
>> >> synchronization
>> >> facility the introduces happens-before relationships that ensure the
>> >> reordering can't take place (at least in the real example!)
>> >>
>> >> As you say, a thread calling methodA() can't tell if anything inside
>> >> methodA() gets reordered, but another thread can tell. That's fine if
>> >> that
>> >> other thread hasn't executed anything that established a happens-before
>> >> relationship with an action in the first thread, but if it has then the
>> >> allowables reorderings are restricted - that is after-all what the
>> >> memory
>> >> model does.
>> >>
>> >> So in your real code each thread stores a result before decrementing
>> >> the
>> >> counter; and each decrement of the count to N+1 happens-before the
>> >> decrement
>> >> to N; hence the storage of the result by the thread that sets N+1,
>> >> happens-before the decrement to N. (Note that you can't tell in what
>> >> order
>> >> the results actually happened, but they both happen before the
>> >> decrement
>> >> to
>> >> N.)
>> >>
>> >> Hope that clarifies things.
>> >>
>> >> David Holmes
>> >>
>> >> >
>> >> > On Sat, Dec 19, 2009 at 7:04 PM, David Holmes wrote:
>> >> > > Enno Shioji writes:
>> >> > >> Now, here is a naive optimization attempt that I think is not
>> >> > thread-safe:
>> >> > >>
>> >> > >> class Task {
>> >> > >>     //Populate with bunch of (Long, new AtomicReference()) pairs
>> >> > >>     //Actual app uses read only HashMap
>> >> > >>     Map<Id, AtomicReference<SubTaskResult>> subtasks =
>> >> > >> populatedMap();
>> >> > >>     AtomicInteger counter = new AtomicInteger(subtasks.size());
>> >> > >>
>> >> > >>     public Task set(id, subTaskResult){
>> >> > >>            //null check omitted
>> >> > >>            subtasks.get(id).set(result);
>> >> > >>            //In the actual app, if !compareAndSet(null, result)
>> >> > >> return null;
>> >> > >>            return check() ? this : null;
>> >> > >>     }
>> >> > >>
>> >> > >>     private boolean check(){
>> >> > >>            return counter.decrementAndGet() == 0;
>> >> > >>     }
>> >> > >>
>> >> > >>   }//class
>> >> > >>
>> >> > >> I concluded a thread can observe a decremented counter (by another
>> >> > >> thread) before the result is set in AtomicReference (by that other
>> >> > >> thread) because of reordering.
>> >> > >
>> >> > > Which "another thread" are you referring to? The AtomicInteger
>> >> > has volatile
>> >> > > semantics and will be read and written by all threads storing a
>> >> > result, so
>> >> > > the results can not appear in the Map after the corresponding
>> >> > decrement of
>> >> > > the counter. For each thread the write to the map happens-before
>> >> > > the
>> >> > > decrement (program order) and each decrement to a non-zero value
>> >> > > must
>> >> > > happen-before the decrement to zero (counter acts as volatile).
>> >> > Consequently
>> >> > > all the results stores must happen-before a zero counter value
>> >> > is seen. I'd
>> >> > > go further and say that any thread that reads the counter value
>> >> > N, must be
>> >> > > able to see the results stored by threads that set a counter
>> >> > value greater
>> >> > > than N.
>> >> > >
>> >> > > David Holmes
>> >> > >
>
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>
>



More information about the Concurrency-interest mailing list