Showing posts with label Concurrency. Show all posts
Showing posts with label Concurrency. Show all posts

Java Concurrency Tips


Executors
executor.execute vs submit
execute can only accept Runnable, execute returns nothing
- void execute(Runnable task)
- used for fire and forget

submit can accept Runnable and Callable, submit returns Future
Future submit(Callable task)
Future submit(Runnable task)
- used when need get the result

Exception handling - if task throws unhanded exception
- execute: the UncaughtExceptionHandler for the Thread running the task to be invoked. By default it prints exception to System.err. We can register global UncaughtExceptionHandler: Thread.setDefaultUncaughtExceptionHandler();
- submit: Any exception thrown will be bound in Future. Call future.get will throw an ExecutionException with the original Throwable as its cause.

RejectedExecutionHandler: ThreadPoolExecutor.CallerRunsPolicy(), NewThreadRunsPolicy

Threadpool
-Isolation: Allocate a separate thread pool for slow web requests within the same application

- corePoolSize 30, maximumPoolSize 50, ArrayBlockingQueue(100): what it means

Deadlock prevention
- Same Lock Ordering
- Lock Timeout

Mutual Exclusion
Hold and Wait
No Preemption

Circular Wait

Lock
Intrinsic Lock - synchronized
- every object and class is logically associated with a monitor
- wait, notify, notifyAll
ReentrantLock
- acquire lock interruptibly and with timeout - tryLock
- lockInterruptibly: used to interrupt thread when it is waiting for lock
- fair lock
- better performance: uses atomic variable and faster CAS operation
- get list of waiting thread for lock
disadvantages
- need put it into try-finally block, code unreadable
- easier to make mistake: forget to unlock in finally block
associate a counter with each thread

Condition
- wait for/notify a particular condition to become true
- can have multiple conditions on same lock
- enables the effect of having multiple wait-sets per object
- factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object
- Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.

- await releases the lock, suspend the thread, signal, signalAll
- BoundedBuffer
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();

Semaphore
- can be released by other threads, useful for deadlock recovery
- P(acquire), V(release)

- a counter (integer) that allows a thread to get into a critical region if the value of the counter is greater than 0
- implementation: no lock, CAS
- acquire/(permits), tryAcquire, release/(permits)
CountDownLatch
CountDownLatch countDownLatch = new CountDownLatch(TOTAL_EVENTS);
countDownLatch.countDown();
countDownLatch.await();
Barrier
CyclicBarrier
- We can reuse CyclicBarrier even if Barrier is broken but can't reuse CountDownLatch

Programming Concurrency on the JVM
Read-write lock - ReentrantReadWriteLock
Thread local storage
Prefer local thread variables over static and shared when possible
Avoiding deadlocks by ordering the locks
Using atomic variables instead of synchronization
Holding locks for as short a time as possible
Avoid executing inside the critical section the code you don't control.

Use lock instead of synchronize
Use CyclicBarrier and CountdownLatch instead of wait/notify

volatile
- visibility: any thread that reads a field will see the most recently written value
- operations on it not be reordered

- not cached in registers or in caches; always read from and write to main memory - thus a little slower

Ensure Visibility
- cross the memory barrier on getters, use volatile, atomicXXX, readlock etc
Ensure Atomicity

Don't start threads in constructors
- use static factory methods, thread pool
Use tryLock - instead of lock
Cancel a task - future.cancel, executor.cancel

Initialization-on-demand holder idiom
Double-checked locking

AtomicLongArray
The long elements in the AtomicLongArray can be updated atomically
- compareAndSet: compare the value of a given element with a specified value, and if the two values are equal, set a new value for that element.
- getAndAdd(index, incrment), getAndIncrement(index), incrementAndGet

AtomicReferenceArray
AtomicReference

Concurrent data structures
ConcurrentHashMap
- guarantees O(log(n)) operations
- sorted, ceilingEntry/Key, floorEntry/Key

ConcurrentSkipListMap
ConcurrentSkipListSet
- thread-safe implementation of the SortedSet
- concurrent crud
- slower than TreeSet, don't use it unless in highly ...
- size is slow, O(n)
- head, nil are in every skip list
- when add, a random number generator is used to determine the levels of the new node
ConcurrentLinkedDeque
CopyOnWriteArrayList
CompletionService
extend thread class vs implement runnable interface
- use threadpool to share threads if implement runnable

Missed Signals - link
boolean wasSignalled = false;
synchronized(myMonitorObject){
  while(!wasSignalled){
    try{
      myMonitorObject.wait();
     } catch(InterruptedException e){...}
  }
  wasSignalled = false;
}

Don't call wait() on constant String's or global objects
LongAdder
- use more memory
- prefer AtomicLong when contention is low

unsafe

unsafe.getAndAddLong(this, valueOffset, 1L)
Practices
Implement AtomicInteger
synchronized
int currentValue;
int previousValue;

Implement ReadWriteLock and ReentrantReadWriteLock - link
private Map readingThreads =
     new HashMap();

 private int writeAccesses    = 0;
 private int writeRequests    = 0;
 private Thread writingThread = null;
Implement Lock and ReEntrantLock
int lockHoldCount;
long IdOfThreadCurrentlyHoldingLock;
IdOfThreadCurrentlyHoldingLock=Thread.currentThread().getId();
synchronized

Implement Semaphore
int permits; synchronized
while(this.permits == 0) wait();

Implement CyclicBarrier
2 or more threads wait for each other to reach a common barrier point. When all threads have reached common barrier point (i.e. when all threads have called await() method), all waiting threads are released.
CyclicBarrier(int parties)

int initialParties; //total parties
int partiesAwait; //parties yet to arrive
synchronized await
-
partiesAwait--;
if(partiesAwait>0){
  this.wait();
}

Implementing (Thread Safe) Stack - link
- AtomicReference
- CAS
- if(head.compareAndSet(oldHead, newHead))
Design threadsafe real time counter
private AtomicLongArray counter = new AtomicLongArray(GRANULARITY);
private volatile int pos = 0;
- Another thread that update pos every second
void incrementPosition(){
    counter.set((pos + 1)%GRANULARITY, 0);
    pos = (pos + 1)%GRANULARITY;

}

Person A holds A, PB holds B, PC holds C, how can we guarantee always print ABC
- Use Semaphore

Print even and odd numbers using threads
Dining Philosophers Problem

Java Lambda-Enabled Concurrency - Don't Use Mutable Variables


What's wrong in following code?
The following code tries to submit all query task to threadpool, but it fails to do that - It only reads part of data.
private List<Future<List<T>>> findAllAsyncImpl(final SolrParams params, final int readSize,
        final int totalCount) {
    final ModifiableSolrParams query = new ModifiableSolrParams(params);
    query.set(CommonParams.ROWS, readSize);
    final List<Future<List<T>>> futures = new ArrayList<>(totalCount);
    int start = 0;
    while (start < totalCount) {
        query.set(CommonParams.START, start);
        futures.add(executor.submit(() -> querySolr(new ModifiableSolrParams(query))));
        start += readSize;
    }
    return futures;
}

I checked the log - what I expected is that it runs query like start=0, 1000, 2000, until totalCount-1000 (readSize is 1000)-  but it's not, it runs query with some start value (here start=45000) multiple times, some start value 0 time.
AbstractSolrRepository querySolr: q=omitted...&rows=1000&start=45000
AbstractSolrRepository querySolr: q=omitted...&rows=1000&start=45000
AbstractSolrRepository querySolr: q=omitted...&rows=1000&start=45000
AbstractSolrRepository querySolr: q=omitted...&rows=1000&start=45000

What the code is doing
Now let's change the lambda expression to the anonymous class() - Eclipse quick fix(Command +1) then select "convert to anonymous class creation".
The second part code creates explicit callable.


while (start < totalCount) {
    query.set(CommonParams.START, start);
    futures.add(executor.submit(new Callable<List<T>>() {
        @Override
        public List<T> call() throws Exception {
            return querySolr(new ModifiableSolrParams(query));
        }
    }));
    start += readSize;
}


while (start < totalCount) {
    query.set(CommonParams.START, start);
    futures.add(executor.submit(new SolrQueryTask(query)));
    start += readSize;
}

private class SolrQueryTask implements Callable<List<T>> {
    private final ModifiableSolrParams query;
    public SolrQueryTask(final ModifiableSolrParams query) {
        this.query = query;
    }
    public List<T> call() throws Exception {
        return querySolr(new ModifiableSolrParams(query));
    }
}
Now the root cause is kind of clear: the task is running in a different thread(for example at that time start=0), it refers variable whose state is changed later,  when the task is actually running, the query variable is already changed.

The solution
private List<Future<List<T>>> findAllAsync
    (final SolrParams params, final int readSize, final int totalCount) {
    final List<Future<List<T>>> futures = new ArrayList<>(totalCount);
    int start = 0;
    while (start < totalCount) {
        final ModifiableSolrParams targetParams =
        new ModifiableSolrParams(params).set(CommonParams.ROWS, readSize).set(CommonParams.START, start);

        futures.add(executor.submit(() -> querySolr(targetParams)));
        start += readSize;
    }
    return futures;
}

Java Lambda-Enabled Concurrency - Variables may be Changed Unexpectedly


In my last post, I explained that Don't Use Mutable Variables in Java Lambda Concurrency, but sometimes, variables may be changed unexpectedly.

The Problem
When optimize performance of function that exports solr data as csv, I add the logger.debug statements, then find out the test throws ConcurrentModificationException:
Caused by: java.util.ConcurrentModificationException: null
at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:711) ~[na:1.8.0_40]
at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:744) ~[na:1.8.0_40]
at java.util.LinkedHashMap$LinkedEntryIterator.next(LinkedHashMap.java:742) ~[na:1.8.0_40]
at org.apache.solr.common.params.ModifiableSolrParams.toString(ModifiableSolrParams.java:201) ~[solr-solrj-5.2.1.jar:5.2.1
at java.text.MessageFormat.subformat(MessageFormat.java:1280) ~[na:1.8.0_40]
at java.text.MessageFormat.format(MessageFormat.java:865) ~[na:1.8.0_40]
at java.text.Format.format(Format.java:157) ~[na:1.8.0_40]
at java.text.MessageFormat.format(MessageFormat.java:841) ~[na:1.8.0_40]
at AbstractSolrRepository.findAllAsyncImpl(AbstractSolrRepository.java:236)

The code looks like below:
@Nonnull private List<Future<List<T>>> findAllAsync(final SolrParams originalParams, final int readSize,
        final long totalCount) {
    final ModifiableSolrParams query = new ModifiableSolrParams(originalParams);
    query.set(CommonParams.ROWS, readSize);
    final List<Future<List<T>>> futures = new ArrayList<>();
    int start = 0;
    while (start < totalCount) {
        final ModifiableSolrParams solrParams = new ModifiableSolrParams(query);
        solrParams.set(CommonParams.START, start);
        final Stopwatch stopWatch = Stopwatch.createStarted();

        futures.add(executor.submit(() -> querySolr(solrParams)));
        if (logger.isDebugEnabled()) {
            logger.debug(MessageFormat.format("executor.submit took {0}, params: {1}",
                    stopWatch.elapsed(TimeUnit.MILLISECONDS), solrParams));
        }
        start += readSize;
    }
    return futures;
}
-- the code is much simplified later. Check final code here.
We submit the query task into thread pool, and want to log how long the submit takes - when all threads in the thread pool are busy, and its queue is full, the submit will block. I want to figure out whether it happens, whether I need change the thread pool settings.
-- Check AsyncConfigurer configuration code here.

It feels strange, so I decide to figure out why.

The Root Cause
ModifiableSolrParams uses LinkedHashMap to store data, its toString loops the LinkedHashMap. 
From the exception, it means that the ModifiableSolrParams is changed during the for-loop in toString() method.

So I check what changed in ModifiableSolrParams, find out solrClient.query adds _stateVer_=cobradb.survey_response:2903.

Then I go to solr github repos and search _stateVer_, and find CloudSolrClient and public static final String STATE_VERSION = "_stateVer_";.

Then I add a break point to places where STATE_VERSION is used and find the following code:
org.apache.solr.client.solrj.impl.CloudSolrClient.requestWithRetryOnStaleState(SolrRequest, int, String)
    if (request.getParams() instanceof ModifiableSolrParams) {
      ModifiableSolrParams params = (ModifiableSolrParams) request.getParams();
      if (stateVerParam != null) {
        params.set(STATE_VERSION, stateVerParam);
      } else {
        params.remove(STATE_VERSION);
      }
    }

The ConcurrentModificationException will happen when the code above and the for-loop in ModifiableSolrParams.toString runs at same time.

Search - Fix same problem
Next I search logger.*query and logger.*param to check whether we made same mistake.

Java Concurrency


- synchronized

- lock, condition
- synchronizedList/Set/Map
- Optimistic Lock: AtomicXXX

- Threadpool
Wait, Notify(All)
- check condition again after wake up and in a loop
- while(condition is not true) obj.wait();
- missed signals
- spurious wakeups


Lock
- replaces synchronized
Condition
- suspend thread's execution, until the condition is true.
- a lock can have multiple (conditions) wait-sets per object
- replaces monitor methods
ReentrantLock

- interruptible, timeout(tryAcquire)


Semaphore

- n permits, acquire, release
- Limiting concurrent access

CountDownLatch
- countDown, await
- can't reuse
- work even if countDown is called before await (unlike wait, notify - missing notification)

CyclicBarrier
- synchronize some tasks at a common point
- reusable

Double-checked locking

- check null again inside the critical block

Volatile

- modification to the field is immediately seen by other threads
- not cached in registers, force main memory access
- no reorder
- happens-before
any memory writes by one statement are visible to another statement.
Use Volatile
- When Writes Do Not Depend on Its Current Value
- Use Volatile Fields for Reading and Locks for Writing
Implementing CopyOnWriteArrayList
- private transient volatile Object[] array;

- Use lock in set(index, element)

AtomicXXX
- CAS
- One instruction, retry infinitely until it succeeds


ConcurrentHashMap
ConcurrentLinkedQueue
- CAS, headNode, taiNode

How to Prevent Deadlock

- same lock ordering
- lock timeout, tryAcquire

Implementing
- Threads that print odd, even numbers
BoundedBuffer
BlockingQueue

- drainTo

- Condition: notFull, notEmpty
Implement Lock using synchronized
Implement ReadWriteLock
Implementing (Thread Safe) Stack
- AtomicReference, CAS
Develop a software to count number of events in last 5 mins
- AtomicLongArray, PositionUpdater
counter.set((pos + 1)%GRANULARITY, 0);
pos = (pos + 1)%GRANULARITY;

Write a program which keeps min and max value in threadsafe way

- AtomicInteger
H2O
Blocking Queue
LinkedBlockingQueue
ArrayBlockingQueue


PriorityBlockingQueue
DelayQueue


ThreadPool

newCachedThreadPool
- Use SynchronousQueue, may use too many threads
newFixedThreadPool, newSingleThreadExecutor
- Use LinkedBlockingQueue, may use too much memory
- Be cautious about unbounded resource - thread, memory

Labels

ANT (6) Algorithm (69) Algorithm Series (35) Android (7) Big Data (7) Blogger (14) Bugs (6) Cache (5) Chrome (19) Code Example (29) Code Quality (7) Coding Skills (5) Database (7) Debug (16) Design (5) Dev Tips (63) Eclipse (32) Git (5) Google (33) Guava (7) How to (9) Http Client (8) IDE (7) Interview (88) J2EE (13) J2SE (49) JSON (7) Java (186) JavaScript (27) Learning code (9) Lesson Learned (6) Linux (26) Lucene-Solr (112) Mac (10) Maven (8) Network (9) Nutch2 (18) Performance (9) PowerShell (11) Problem Solving (11) Programmer Skills (6) Scala (6) Security (9) Soft Skills (38) Spring (22) System Design (11) Testing (7) Text Mining (14) Tips (17) Tools (24) Troubleshooting (29) UIMA (9) Web Development (19) Windows (21) adsense (5) bat (8) regex (5) xml (5)