I'm trying to stimulate the scenario of deadlocks in the shared array using Reentrant Locks.
class SharedArray {
private int ff[];
private Lock keys[];
public SharedArray(int n){
ff = new int[n];
keys = new ReentrantLock[n];
for(int j = 0; j < n; j++){
ff[j] = (int)(Math.random()*100);
keys[j] = new ReentrantLock();
}
}
void swap(int j, int k) {
keys[j].lock(); keys[k].lock();
int t = ff[j]; ff[j] = ff[k]; ff[k] = t;
keys[j].unlock(); keys[k].unlock();
}
}
Here the swap method is deadlock prone which I have achieved. for example if Thread 1 is swap(7,4) and at the same time Thread 2 is swap(4,7) this will raised the deadlock.
How do I prevent it from deadlock. What sort of refactoring is required. I have tried to used synchronized but I'm looking for possibly a reliable way to solve that.
synchronized void swap(int j, int k) {
keys[j].lock(); keys[k].lock();
int t = ff[j]; ff[j] = ff[k]; ff[k] = t;
keys[j].unlock(); keys[k].unlock();
}
How do I prevent it from deadlock?
One way to prevent deadlock would be to ensure that any threads that acquire the same two locks always will acquire them in the same order.
void swap(int j, int k) {
int first = Math.min(j, k);
int second = Math.max(j, k);
keys[first].lock(); keys[second].lock();
int t = ff[j]; ff[j] = ff[k]; ff[k] = t;
keys[second].unlock(); keys[first].unlock();
}
You want to employ lock ordering. If you lock each time in a predictable order you can prevent dead locking.
Found in Java Concurrency In Practice, you can see an example of how to achieve this:
public void transferMoney(final Account fromAcct, final Account toAcct, final DollarAmount amount)
throws InsufficientFundsException {
class Helper {
public void transfer() throws InsufficientFundsException {
if (fromAcct.getBalance().compareTo(amount) < 0)
throw new InsufficientFundsException();
else {
fromAcct.debit(amount);
toAcct.credit(amount);
}
}
}
int fromHash = System.identityHashCode(fromAcct);
int toHash = System.identityHashCode(toAcct);
if (fromHash < toHash) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
} else if (fromHash > toHash) {
synchronized (toAcct) {
synchronized (fromAcct) {
new Helper().transfer();
}
}
} else {
synchronized (tieLock) {
synchronized (fromAcct) {
synchronized (toAcct) {
new Helper().transfer();
}
}
}
}
}
https://pdfs.semanticscholar.org/3650/4bc31d3b2c5c00e5bfee28ffc5d403cc8edd.pdf, search for Listing 10.3. Inducing a Lock Ordering to Avoid Deadlock.
Related
I write a simple ringbuffer and in method test1() I use one thread is poll() and
one thread is offer(). I test many time but it is always true. Can you explain for me?
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
#SuppressWarnings("unchecked")
public class RingBuffer<T> {
private T[] buffer;
// private volatile T[] buffer;
private int readIndex;
private int writeIndex;
private final int capacity;
private AtomicInteger size;
public RingBuffer(int k) {
this.buffer = (T[]) new Object[k];
this.capacity = k;
this.readIndex = 0;
this.writeIndex = 0;
this.size = new AtomicInteger(0);
}
public boolean offer(T value) {
if (isFull()) return false;
buffer[writeIndex] = value;
writeIndex++;
if (writeIndex == capacity) writeIndex -= capacity;
size.getAndIncrement();
return true;
}
public T poll() {
if (isEmpty()) return null;
int index = readIndex;
T x = buffer[index];
readIndex++;
if (readIndex == capacity) readIndex -= capacity;
size.getAndDecrement();
return x;
}
public boolean isEmpty() {
return size.get() == 0;
}
public boolean isFull() {
return size.get() == capacity;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
test1();
}
}
As you see in the test1() method I use different Thread but the check is true.
Sorry because stackoverflow warning me this question is mostly code so I will paste test1() method in here.
public static void test1() throws ExecutionException, InterruptedException {
RingBuffer<String> buffer = new RingBuffer<>(1000);
AtomicBoolean writeDone1 = new AtomicBoolean(false);
ExecutorService service = Executors.newFixedThreadPool(2);
ExecutorService service1 = Executors.newFixedThreadPool(2);
Callable<List<String>> cw1 = () -> {
List<String > x = new ArrayList<>();
int count = 0;
for (int i = 0; i < 10000000; i++) {
if (buffer.offer( i+"")) {
count++;
x.add(i+"");
}
}
writeDone1.set(true);
System.out.println("num write " + count);
return x;
};
Callable<List<String>> cr = () -> {
List<String> x = new ArrayList<>();
int count = 0;
while (!writeDone1.get()) {
String data = buffer.poll();
if (data != null) {
x.add(data);
count++;
}
}
while (true) {
String data = buffer.poll();
if (data != null) {
x.add(data);
count++;
} else {
break;
}
}
System.out.println("num read " + count);
return x;
};
Future<List<String >> fw = service.submit(cw1);
Future<List<String>> fr = service1.submit(cr);
List<String> sw = fw.get();
List<String> sr = fr.get();
System.out.println(sw.size());
System.out.println(sr.size());
boolean check = true;
for (int i =0 ; i< sw.size() ; i++){
if (!sw.get(i).equals( sr.get(i))){
check = false;
break;
}
}
System.out.println(check);
service.shutdown();
service1.shutdown();
}
If I use only one consumer and producer. I can't write a test make the race conditions here. Can you help me?
Thankyou
If there are one consumer and one producer, then this RingBuffer is thread-safe.
Happens-before is provided by AtomicInteger size: it is read at the start and is written at the end of both poll() and offer().
For example, let's look at poll().
Notice that:
in poll() we read buffer[index] only if we've read size.get()!=0
size.get()!=0 can only happen after size.getAndIncrement() in offer()
size is AtomicInteger, which means it provides happens-before and makes all modifications in offer() visible in poll()
In other words:
buffer[writeIndex]=value in offer()
-(happens-before)-> size.getAndIncrement() in offer()
-(happens-before)-> size.get()!=0 in poll()
-(happens-before)-> T x = buffer[index] in poll()
There is no happens before edge between a write to an array at some position and a read from the same position. So if you don't have any ordering guarantee in place, your code is suffering from a data race.
If you also allow for concurrent offers and concurrent polls, then you also have race conditions on your hands.
It has been quite some time I played with ringbuffers. But normally you make use of a tail and head sequence (e.g. a long). If you make the ringbuffer a power of 2, you can do a cheap mod on the conversion of the sequences to indices. And the head and tail sequence could be relatively expensive volatiles (I really would start with that) and later on you could play with relaxed memory order modes. The head and tail will give you the appropriate happens before edges so don't need to do anything special to the array. With this approach you can also get rid of the 'size'; you can calculate the size as the difference between tail and thehead; the problem with size is that it will cause contention between a thread read/writing to the ringbuffer. Also you need to properly pad the the head/tail fields to prevent false sharing.
The program's aim is to simulate that multiple users add a number to the buffer from 0 to n. Then print the sum of numbers in the buffer. When I run the program, it seems that the threads never end. However, the thread will finish when I run the program in debug mode of Idea and step line by line. Also, I do not exactly know where I need to use my semaphore method P() and V() for mutual exclusion.
Version: JDK 8. I cannot use semaphore in the library.
Main.java
Buffer b = new Buffer(bufferSize);
ArrayList<user> us = new ArrayList<>();
for(int i = 0; i < num_users; i++) us.add(new user(i, elements, b));
ArrayList<Thread> th = new ArrayList<>();
for(int i = 0; i < num_users; i++)
{
th.add(new Thread(us.get(i)));
th.get(i).start();
}
user.java
public class user implements Runnable
{
private int id;
private int num_elements;
private semaphore mutex = new semaphore(1 );
public static Buffer buf;
public user(int i, int el, Buffer b)
{id = i; num_elements = el; buf = b;}
public void add_elements()
{//Add element to buffer, element value iterates from 0, 1, 2 .... num_elements
mutex.P();
int n = 0;
while (num_elements > 0)
{
buf.add(new Integer(n));
n++;
num_elements--;
}
mutex.V();
}
public void run()
{
add_elements();
}
}
Buffer.java
public class Buffer
{
private LinkedList<Object> buf_list;
private int elements; //Number of elements currently on the queue
private int buf_size; //Maximum number of elements allowed on queue
private semaphore mutex = new semaphore(1);
public Buffer(int n) //Queue creation, with n indicating the maximum capacity
{
buf_list = new LinkedList<Object>();
elements = 0;
buf_size = n;
}
public void add(Integer n)
{
mutex.P();
buf_list.add(n);
elements++;
mutex.V();
}
public void finalSummation()
{
if (elements == buf_size)
{
mutex.P();
int sum = 0;
for (Object n : buf_list)
sum += ((Integer)n).intValue();
mutex.V();
System.out.println("Count total: " + sum);
}
}
}
semaphore.java
public class semaphore
{
private int count = 0;
public semaphore(int init_count)
{
count = init_count;
}
public synchronized void P()
{
count -= 1;
while (count < 0)
{
try {
wait();
} catch (InterruptedException e) {
System.out.println("Error");
System.exit(-1);
}
}
}
public synchronized void V()
{
count += 1;
notifyAll();
}
}
I expect it will print the sum of buffer numbers, but the thread may not finish.
There are a few things that stand out as issues here.
1) your code is never calling the finalSummation method. So the "printing" of the result will never happen.
2) Buffer and each user are all creating their own semaphores. If you are attempting to allow multiple threads to update Buffer without colliding then you need to share the same semaphore. Remove the semaphore and the usage of it from the user class. Just let the Buffer instance control only one update at a time with its semaphore.
3) You don't need to check the semaphore in the finalSummation method. Presumably, all threads are done at that point. And to enforce that ...
4) Put code like this at the end of main
for(int i = 0; i < num_users; i++) {
th.get(i).join();
}
b.finalSummation();
5) A semaphore should manage a number of permits. Your semaphore is managing the number of instances waiting - that is a pretty much an irrelevant number for a semaphore. Change your P() and V() to acquire() and release() to be consistent with the pattern.
public static class semaphore {
private int permits = 0;
public semaphore(int permits) {
this.permits = permits;
}
public synchronized void acquire() {
while (permits < 1) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("Error");
System.exit(-1);
}
}
permits--;
}
public void release() {
synchronized (this) {
permits += 1;
notifyAll();
}
}
}
I have put it together using the above answer if that helps. Please select above answer as the right answer.
import java.util.ArrayList;
import java.util.LinkedList;
public class Main {
// assumed values
private static final int bufferSize = 5;
private static final int num_users = 10;
private static final int elements = 5;
public static void main(String[] args) {
Buffer b = new Buffer(bufferSize);
ArrayList<User> us = new ArrayList<>();
for(int i = 0; i < num_users; i++) us.add(new User(i, elements, b));
ArrayList<Thread> th = new ArrayList<>();
for(int i = 0; i < num_users; i++)
{
th.add(new Thread(us.get(i)));
th.get(i).start();
}
for(int i = 0; i < num_users; i++) {
try {
th.get(i).join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
b.finalSummation();
System.out.println("Exiting");
}
}
class User implements Runnable
{
private int id;
private int num_elements;
public static Buffer buf;
public User(int i, int el, Buffer b)
{id = i; num_elements = el; buf = b;}
public void add_elements()
{//Add element to buffer, element value iterates from 0, 1, 2 .... num_elements
int n = 0;
while (num_elements > 0)
{
buf.add(new Integer(n));
n++;
num_elements--;
}
}
public void run()
{
add_elements();
}
}
class Buffer
{
private LinkedList<Object> buf_list;
private int elements; //Number of elements currently on the queue
private int buf_size; //Maximum number of elements allowed on queue
private Semaphore mutex ;
public Buffer(int n) //Queue creation, with n indicating the maximum capacity
{
buf_list = new LinkedList<Object>();
elements = 0;
buf_size = n;
mutex = new Semaphore(buf_size);
}
public synchronized void add(Integer n)
{
mutex.acquire();
buf_list.add(n);
elements++;
mutex.release();
}
public void finalSummation()
{
int sum = 0;
System.out.println(buf_list);
for (Object n : buf_list)
sum += ((Integer)n).intValue();
System.out.println("Count total: " + sum);
}
}
class Semaphore {
private int permits = 0;
public Semaphore(int permits) {
this.permits = permits;
}
public synchronized void acquire() {
while (permits < 1) {
try {
wait();
} catch (InterruptedException e) {
System.out.println("Error");
System.exit(-1);
}
}
permits--;
}
public void release() {
synchronized (this) {
permits += 1;
notifyAll();
}
}
}
I've read a lot about thread-safety. In certain part of my multi-threaded program, I preferred to try the immutability. After getting incorrect results, I noticed my immutable object is not thread-safe although it is 100% immutable. Please correct me if I'm wrong.
public final class ImmutableGaugeV4 {
private final long max, current;
public ImmutableGaugeV4(final long max) {
this(max, 0);
}
private ImmutableGaugeV4(final long max, final long current) {
this.max = max;
this.current = current;
}
public final ImmutableGaugeV4 increase(final long increment) {
final long c = current;
return new ImmutableGaugeV4(max, c + increment);
}
public final long getCurrent() {
return current;
}
public final long getPerc() {
return current * 100 / max;
}
#Override
public final String toString() {
return "ImmutableGaugeV4 [max=" + max + ", current=" + current + "](" + getPerc() + "%)";
}
}
aaaaa
public class T4 {
public static void main(String[] args) {
new T4().x();
}
ImmutableGaugeV4 g3 = new ImmutableGaugeV4(10000);
private void x() {
for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 1000; j++) {
g3 = g3.increase(1);
System.out.println(g3);
}
}
}.start();
}
}
}
Sometimes I'm getting correct results, and most of the times I'm not
ImmutableGaugeV4 [max=10000, current=9994](99%)
ImmutableGaugeV4 [max=10000, current=9995](99%)
ImmutableGaugeV4 [max=10000, current=9996](99%)
ImmutableGaugeV4 [max=10000, current=9997](99%)
What is wrong with this immutable object? What is missing to make it thread-safe without using intrinsic locks?
Neither
final long c = current;
return new ImmutableGaugeV4(max, c + increment);
nor
g3 = g3.increase(1);
is thread-safe. These compound actions aren't atomic.
I recommend reading "Java concurrency in practice" by Brian Goetz: the chapters devoted to compound actions and "publication and escape" problems.
Your problem is that you are not using thread safe operations for your numeric variables max and current. Because of that, many threads can get the same value from them even tough it has already been changed.
You could add synchronized blocks to handle reading / writing to them, but the best approach is to use thread safe classes to handle that for you.
If you need long values, that would be AtomicLong. Take a look at it’s documentation, it has methods to do the operations you want.
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicLong.html
Whenever you’re multithreading you should go for threadsafe objects, such as the Atomic family, ConcurrentHashMap for maps, and so on.
Hope it helps!
The only problem here is the following line:
g3 = g3.increase(1);
This is equivalent to the following lines:
var tmp = g3;
tmp = tmp.increase(1);
g3 = tmp;
To fix this, you could use a Compare And Swap:
private static final VarHandle G3;
static {
try {
G3 = MethodHandles.lookup().findVarHandle(T4.class, "g3", ImmutableGaugeV4.class);
} catch (ReflectiveOperationException roe) {
throw new Error(roe);
}
}
And then replace g3 = g3.increase(1); with:
ImmutableGaugeV4 oldVal, newVal;
do {
oldVal = g3;
newVal = oldVal.increase(1);
} while (!G3.compareAndSet(T4.this, oldVal, newVal));
System.out.println(newVal);
In the end, your T4 becomes:
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class T4 {
public static void main(String[] args) {
new T4().x();
}
ImmutableGaugeV4 g3 = new ImmutableGaugeV4(10000);
private static final VarHandle G3;
static {
try {
G3 = MethodHandles.lookup().findVarHandle(T4.class, "g3", ImmutableGaugeV4.class);
} catch (ReflectiveOperationException roe) {
throw new Error(roe);
}
}
private void x() {
for (int i = 0; i < 10; i++) {
new Thread() {
public void run() {
for (int j = 0; j < 1000; j++) {
ImmutableGaugeV4 oldVal, newVal;
do {
oldVal = g3;
newVal = oldVal.increase(1);
} while (!G3.compareAndSet(T4.this, oldVal, newVal));
System.out.println(newVal);
}
}
}.start();
}
}
}
I read now Thinking in Java, chapter about atomicity and visibility. There is an example I don't understand.
public class SerialNumberGenerator {
private static volatile int serialNumber = 0;
public static int nextSerialNumber() {
return serialNumber++;
}
}
class CircularSet {
private int[] array;
private int len;
private int index = 0;
public CircularSet(int size) {
array = new int[size];
len = size;
for (int i = 0; i < size; i++) {
array[i] = -1;
}
}
synchronized void add(int i) {
array[index] = i;
index = ++index % len;
}
synchronized boolean contains(int val) {
for (int i = 0; i < len; i++) {
if (array[i] == val)
return true;
}
return false;
}
}
public class SerialNumberChecker {
private static final int SIZE = 10;
private static CircularSet serials = new CircularSet(1000);
private static ExecutorService exec = Executors.newCachedThreadPool();
static class SerialChecker implements Runnable {
#Override
public void run() {
while (true) {
int serial = SerialNumberGenerator.nextSerialNumber();
if (serials.contains(serial)) {
System.out.println("Duplicate: " + serial);
System.exit(0);
}
serials.add(serial);
}
}
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < SIZE; i++) {
exec.execute(new SerialChecker());
}
}
}
example output:
Duplicate: 228
I don't understand how is it possible. Even method nextSerialNumber() is not synchronized and all thread generate different values each thread has own value of serial and each are different. So how is it possible to find duplicate. I cannot imagine of threads execution.
This example shows the post-increment operator is not atomic and not thread-safe.
What happens in this code is:
many (up to 100) threads are started, each executing the same code
in an infinite loop:
an unsynchronized method nextSerialNumber is called, which returns the result of the post-increment operator called on a static variable
a synchronized method contains is called, which checks if the returned value exists in the underlying collection
if yes, the program is terminated
if not, the value is added to the underlying collection
If the post-increment operation was thread-safe then the program would never print "Duplicate" and would never terminate,
since every thread would be getting a different serial number value. This is not the case as two threads
might get exactly the same serial number value.
I've implemented the resource hierarchy solution to the Dining Philosopher's Problem. When I try to compare the two Chopsticks' n values, they end up in deadlock. However, if I use their hashCodes instead of n values, it runs smoothly. Why this difference? Aren't they both numbers at the end of the day?
import java.util.Random;
class Chopstick {
public final int n;
public Chopstick(int n) {
this.n = n;
}
}
class Philosopher extends Thread {
private Chopstick left, right;
private Random random;
private final int n;
public Philosopher(int n, Chopstick left, Chopstick right) {
this.n = n;
if (left.n > right.n) { // no deadlock if I replace this with left.hashCode() > right.hashCode()
this.right = left;
this.left = right;
} else {
this.left = left;
this.right = right;
}
this.random = new Random();
}
#Override
public void run() {
try {
while (true) {
Thread.sleep(random.nextInt(10)); // Think
synchronized(left) {
synchronized(right) {
System.out.println("P " + n + " eating");
Thread.sleep(random.nextInt(10));
}
}
}
} catch(InterruptedException ie) {
ie.printStackTrace();
}
}
}
class Main {
public static void main(String[] args) {
final int n = 3;
Chopstick[] sticks = new Chopstick[n];
Philosopher[] ps = new Philosopher[n];
for (int i = 0; i < n; i++) {
sticks[i] = new Chopstick(n);
}
for (int i = 0; i < n; i++) {
ps[i] = new Philosopher(i, sticks[i], sticks[(i + 1) % n]);
ps[i].start();
}
}
}
Your problem is related to the fact that you don't manage cases where left.n == right.n and unfortunately instead of initializing your Chopstick array with sticks[i] = new Chopstick(i), you used sticks[i] = new Chopstick(n) such that you have only cases of type left.n == right.n which are not properly managed so you get deadlocks.
As you did not override the method hashCode(), using hashCode() helps to avoid the problem because they are different instances of Chopstick with different values of hashCode() but you could still meet cases where we have 2 different instances of Chopstick with the same hashCode(). So you still have to manage cases where we have equal values.
The way to manage equal values properly is by using a third lock called the "tie breaking" lock
class Philosopher extends Thread {
// The tie breaking lock
private static Object tieLock = new Object();
...
private void printAndSleep() throws InterruptedException {
synchronized(left) {
synchronized(right) {
System.out.println("P " + n + " eating");
Thread.sleep(random.nextInt(10));
}
}
}
public void run() {
...
if (left.n == right.n) {
// Equal values so we need first to acquire the tie breaking lock
synchronized (tieLock) {
printAndSleep();
}
} else {
printAndSleep();
}
...
}
}
A more generic way to manage lock ordering is by relying on System.identityHashCode(obj) as value of each instance to sort instead of using a field's value or hashCode() because this way you won't depend on something specific to the target object's type.
More details in chapter 10.1.2 Dynamic lock order deadlock of Java Concurrency in Practice from Brian Goetz
The BUG is that you have
sticks[i] = new Chopstick(n);
when it should be
sticks[i] = new Chopstick(i);
The hash values of the objects will still be unique even if their data is the same since you haven't overridden the hashCode function.