Introduction to Java Concurrency Problems
Java, with Concurrency API, gives good solutions to deadlock problem: the use of blocks with java.util.concurrent.locks
interface and its implementations, such as ReentrantLock
class; whereas to solve livelock problem there are not too many documentations online.
Starvation is another problem linked to concurrency, see references section to get in deep.
What is Livelock
A definition of Livelock, from Lessons: Concurrency:
A thread often acts in response to the action of another thread. If the other thread's action is also a response to the action of another thread, then livelock may result. As with deadlock, livelocked threads are unable to make further progress. However, the threads are not blocked — they are simply too busy responding to each other to resume work. This is comparable to two people attempting to pass each other in a corridor: Alphonse moves to his left to let Gaston pass, while Gaston moves to his right to let Alphonse pass. Seeing that they are still blocking each other, Alphone moves to his right, while Gaston moves to his left. They're still blocking each other, so...
My Solution
The solution that I propose uses java.util.concurrent.ExecutorService
interface and LiveLockSolution
interface that I created.
How to Use LiveLockSolution Interface
With LiveLockSolution
, I'll remember to code a method to solve livelock into classes used for datas concurrency.
public interface LiveLockSolution {
public void liveLockSolution();
}
And this is the Product
class that uses blocks and implements LiveLockSolution
.
public class Product implements LiveLockSolution{
private int quantity;
private boolean endLoop;
private ReentrantLock lock;
private Condition cond;
public Product(ReentrantLock lock) {
this.endLoop = false;
this.quantity = 0;
this.lock = lock;
this.cond = lock.newCondition();
}
public void produce() {
lock.lock();
try {
this.quantity++;
System.out.println("Q:" + this.quantity);
cond.signalAll();
} finally {
lock.unlock();
}
}
public void consume() {
lock.lock();
try {
while(this.endLoop == false && this.quantity == 0) {
cond.await();
}
if(this.endLoop == false) {
this.quantity--;
System.out.println("Q:" + this.quantity);
}
cond.signalAll();
} catch(Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
@Override
public void liveLockSolution() {
lock.lock();
try {
this.endLoop = true;
cond.signalAll();
} finally {
lock.unlock();
}
}
Consumer
and Productor
classes are present into the zip file now.
How to Use ExecutorService Interface
The ExecutorService
interface is the actor into main code (see next example): using awaitTermination
method, after stopping all threads, we know if there are running threads into executor pool and so use liveLockSolution
to solve livelock problem.
public static void main(String[] args) {
try {
ReentrantLock lock = new ReentrantLock();
Product obj = new Product(lock);
Consumer cons = new Consumer(obj);
Consumer cons2 = new Consumer(obj);
Producer prod = new Producer(obj);
System.out.println("Start concurrency\n\n");
ExecutorService es = Executors.newCachedThreadPool();
es.execute(cons);
es.execute(cons2);
es.execute(prod);
Thread.sleep(1000);
prod.stop();
cons.stop();
cons2.stop();
es.shutdown();
while(es.awaitTermination(100, TimeUnit.MILLISECONDS) == false) {
System.out.println("Waiting threads closing");
obj.liveLockSolution();
}
System.out.println("Concurrency end");
} catch (InterruptedException e) {
e.printStackTrace();
} catch(Exception e) {
e.printStackTrace();
}
}
And that is all, folks!
References
History
- First release: November 2011