0

I'd like to show difference in performance RW locks vs synchronized. I've done the lock part, but I got problem with synchronized. The add method is invoked by the Producer threads, the getRandomElement() is invoked by the Consumer threads. I would like to force that the add() method is executed while nobody executes getRandomElement() method, and that getRandomElement() method when nobody executes add() method using synchronized word.

Is it possible?

import java.util.ArrayList;
import java.util.List;

public class ThreadSafeArrayList<E> {

    private final List<E> list = new ArrayList<>();
    private final Object m = new Object();

    public void add(E o) {
        synchronized (m) {
            list.add(o);
            System.out.println("Adding element by thread" + Thread.currentThread().getName());
        }
    }

    public E getRandomElement() {
        synchronized (m) {
            System.out.println("Printing elements by thread" + Thread.currentThread().getName());
            if (size() == 0) {
                return null;
            }
            return list.get((int) (Math.random() * size()));
        }
    }

    public int size() {
        return list.size();
    }

}

RW version which is slower for some reason, but should be faster:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ThreadSafeArrayList<E> {
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    private final Lock readLock = readWriteLock.readLock();

    private final Lock writeLock = readWriteLock.writeLock();

    private final List<E> list = new ArrayList<>();

    public void add(E o) {
        writeLock.lock();
        try {
            list.add(o);
            System.out.println("Adding element by thread" + Thread.currentThread().getName());
        } finally {
            writeLock.unlock();
        }
    }

    public E getRandomElement() {
        readLock.lock();
        try {
            System.out.println("Printing elements by thread" + Thread.currentThread().getName());
            if (size() == 0) {
                return null;
            }
            return list.get((int) (Math.random() * size()));
        } finally {
            readLock.unlock();
        }
    }

    public int size() {
        return list.size();
    }

}

Following classes are exactly the same in RW and synchronized versions.

The Producer class :

public class Producer implements Runnable {
    public final static int NUMBER_OF_OPERATIONS = 100;
    ThreadSafeArrayList<Integer> threadSafeArrayList;

    public Producer(ThreadSafeArrayList<Integer> threadSafeArrayList) {
        this.threadSafeArrayList = threadSafeArrayList;
    }

    @Override
    public void run() {
        for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) {
            threadSafeArrayList.add((int) (Math.random() * 1000));
        }
    }

}

the Consumer class:

public class Consumer implements Runnable {
    public final static int NUMBER_OF_OPERATIONS = 100;
    ThreadSafeArrayList<Integer> threadSafeArrayList;

    public Consumer(ThreadSafeArrayList<Integer> threadSafeArrayList) {
        this.threadSafeArrayList = threadSafeArrayList;
    }

    @Override
    public void run() {
        for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) {
            Integer obtainedElement = threadSafeArrayList.getRandomElement();
        }
    }

}

and the Main:

import java.util.ArrayList;

public class Main {
    public static long start, end;

    public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            end = System.currentTimeMillis();
            System.out.println("Time of execution " + (end - start) + " milisekund");
        }));
        start = System.currentTimeMillis();
        final int NUMBER_OF_THREADS = 100;
        ThreadSafeArrayList<Integer> threadSafeArrayList = new ThreadSafeArrayList<>();
        ArrayList<Thread> consumerThreadList = new ArrayList<Thread>();
        for (int i = 0; i < NUMBER_OF_THREADS; i++) {
            Thread t = new Thread(new Consumer(threadSafeArrayList));
            consumerThreadList.add(t);
            t.start();
        }
        ArrayList<Thread> producerThreadList = new ArrayList<Thread>();
        for (int i = 0; i < NUMBER_OF_THREADS; i++) {
            Thread t = new Thread(new Producer(threadSafeArrayList));
            producerThreadList.add(t);
            t.start();
        }

        //  System.out.println("Printing the First Element : " + threadSafeArrayList.get(1));

    }

}
Yoda
  • 17,363
  • 67
  • 204
  • 344
  • take a look at `wait/notify` – sidgate Dec 23 '15 at 10:13
  • @sidgate Know those, now how they're work, yet I never know how to use them when there are more than 1 producer and 1 consumer. – Yoda Dec 23 '15 at 10:20
  • it would still work, `wait` for consumer and `notifyAll` for producer task – sidgate Dec 23 '15 at 10:23
  • @sidgate Like this: http://wklej.org/id/1884833/ ? I get dead lock. – Yoda Dec 23 '15 at 10:29
  • @Yoda I you are using only one instance of your class(between producers and all consumers) this code should work fine. – gladiator Dec 23 '15 at 11:02
  • It seems to work but for some reason it is faster than RW locks. I'll place a sample in OP in a second. – Yoda Dec 23 '15 at 11:07
  • The `synchronized` is 100ms faster than RW locks. It shouldn't happen. – Yoda Dec 23 '15 at 11:08
  • @Yoda While benchmarking you should do a warmup first, secondly for RW locks if you are taking write lock a lot the performance would not be good check out http://stackoverflow.com/questions/11638233/why-do-javas-synchronized-collections-not-use-read-write-locks for some detail into the reasons for such behaviour – gladiator Dec 23 '15 at 11:34

1 Answers1

0

Below is the approach. You might need to try out and fix the while loop. Also you might need to synchronize the list object instead of the containing class methods

public synchronized void add(E o) {
    try {


        list.add(o);
        notifyAll();
        System.out.println("Adding element by thread" + Thread.currentThread().getName());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public synchronized E getRandomElement() {
    try {
        System.out.println("Printing elements by thread" + Thread.currentThread().getName());
        while(true){
           wait();
           if(list.size()>0) break;
        }
        E ret = list.get((int) (Math.random() * size()));
        return ret;
    } catch (Exception e) {
        return null;
    } 

}
sidgate
  • 14,650
  • 11
  • 68
  • 119
  • It does not compile, after I remove unreachable catch block in add and run the program I get deadlock. – Yoda Dec 23 '15 at 10:37