简体   繁体   中英

Why RW lock is slower by 100ms than synchronized in my example

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));

    }

}

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;
    } 

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM