ReentrantReadWriteLock работает очень плохо по сравнению с ReentrantLock - PullRequest
0 голосов
/ 20 октября 2019

Я создал 1000 потоков для увеличения, 1000 потоков для уменьшения, 1000 потоков для чтения значения.

Каждый поток увеличения увеличивает значение в 25000 раз.

Каждый поток уменьшения уменьшаетсязначение в 25000 раз.

Каждый поток чтения читает значение 50000 раз.

, поэтому над всеми операциями преобладает чтение.

ReadLock устанавливается при чтениизначение

и WriteLock устанавливаются для методов, увеличивающих и уменьшающих значения.

Замечено: ReentrantReadWriteLock занимает около 13000 мс. Блокировка занимает около 3000 мс. Ожидается: ReentrantReadWriteLock даст гораздо более высокую производительность, чем ReentrantLock.

Кстати: лично я считаю, что нет необходимости иметь блокировку / синхронизацию при использовании метода getCounter (просто читая значение)

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Main {
    public static void main(String[] args) throws InterruptedException {

        ArrayList<Thread> reads = new ArrayList<>();
        ArrayList<Thread> increments = new ArrayList<>();
        ArrayList<Thread> decrements = new ArrayList<>();
        Resources resources = new Resources();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
            Thread read = new Read(resources);
            Thread increment = new Increment(resources);
            Thread decrement = new Decrement(resources);
            reads.add(read);
            increments.add(increment);
            decrements.add(decrement);
            read.start();
            increment.start();
            decrement.start();
        }
        for (int i = 0; i < 1000; i++) {
            reads.get(i).join();
            increments.get(i).join();
            decrements.get(i).join();
        }
        System.out.println(resources.getCounter());
        System.out.println(System.currentTimeMillis() - start);
    }

    private static abstract class UserThread extends Thread {
        protected Resources resources;

        public UserThread(Resources resources) {
            this.resources = resources;
        }

    }

    private static class Read extends UserThread {

        public Read(Resources resources) {
            super(resources);
        }

        public void run() {
            for (int i = 0; i < 50000; i++) {
                resources.getCounter();

            }

        }
    }

    private static class Increment extends UserThread {

        public Increment(Resources resources) {
            super(resources);
        }

        public void run() {
            for (int i = 0; i < 25000; i++) {
                resources.increment();

            }

        }
    }

    private static class Decrement extends UserThread {

        public Decrement(Resources resources) {
            super(resources);
        }

        public void run() {
            for (int i = 0; i < 25000; i++) {
                resources.decrement();

            }

        }
    }

    private static class Resources {

        private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

        private ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        private ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        private ReentrantLock lock = new ReentrantLock();

        public int getCounter() {
            readLock.lock();
            try {
                return counter;
            } finally {
                readLock.unlock();
            }

        }

        private int counter = 0;

        public void increment() {
            writeLock.lock();
            try {
                counter++;
            } finally {
                writeLock.unlock();
            }
        }

        public void decrement() {
            writeLock.lock();
            try {
                counter--;
            } finally {
                writeLock.unlock();
            }
        }

    }

}

Ответы [ 2 ]

2 голосов
/ 20 октября 2019

Эти виды блокировок - чтение-запись - обычно оптимизируются и подходят для многих читателей и одного или нескольких авторов. Они часто вращаются на операциях, ожидая, что чтения будут быстрыми, а записи - очень малыми. Кроме того, они оптимизированы для обеспечения справедливости или обработки запросов FIFO во избежание остановки потоков.

Вы делаете с точностью до наоборот. Вы делаете много писателей, которые вызывают чрезмерное вращение и другие сложные подходы, подходящие для сценариев «много читает, мало пишет».

Простые блокировки просты. Они просто блокируют все потоки, когда они готовы, и никакого вращения не происходит. Их недостаток в том, что они вызывают лавинный эффект, когда пробуждают несколько потоков, чтобы они просто снова уснули.

0 голосов
/ 20 октября 2019

Спасибо Нику и Славу за то, что они указали, что они не читаются доминирующими. Я удостоверился, что у меня есть 100 приращений, 100 декрементов и 1000 потоков чтения.

Результаты получены, как и ожидалось. Выход с ReentrantReadWriteLock составляет 300 мс, а withLock - 5000 мс.

Вот модифицированный код

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Main {
    public static void main(String[] args) throws InterruptedException {

        ArrayList<Thread> reads = new ArrayList<>();
        ArrayList<Thread> increments = new ArrayList<>();
        ArrayList<Thread> decrements = new ArrayList<>();
        Resources resources = new Resources();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            Thread increment = new Increment(resources);
            Thread decrement = new Decrement(resources);
            increments.add(increment);
            decrements.add(decrement);
            increment.start();
            decrement.start();
        }

        for (int i = 0; i < 1000; i++) {
            Thread read = new Read(resources);
            reads.add(read);
            read.start();
        }

        for (int i = 0; i < 100; i++) {
            increments.get(i).join();
            decrements.get(i).join();
        }

        for (int i = 0; i < 1000; i++) {
            reads.get(i).join();
        }
        System.out.println(System.currentTimeMillis() - start);
    }

    private static abstract class UserThread extends Thread {
        protected Resources resources;

        public UserThread(Resources resources) {
            this.resources = resources;
        }

    }

    private static class Read extends UserThread {

        public Read(Resources resources) {
            super(resources);
        }

        public void run() {
                resources.getCounter();


        }
    }

    private static class Increment extends UserThread {

        public Increment(Resources resources) {
            super(resources);
        }

        public void run() {
                resources.increment();



        }
    }

    private static class Decrement extends UserThread {

        public Decrement(Resources resources) {
            super(resources);
        }

        public void run() {
                resources.decrement();



        }
    }

    private static class Resources {

        private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

        private ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        private ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        private ReentrantLock lock = new ReentrantLock();

        public int getCounter() {
            readLock.lock();
            try {
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return counter;
            } finally {
                readLock.unlock();
            }

        }

        private int counter = 0;

        public void increment() {
            writeLock.lock();
            try {
                counter++;
            } finally {
                writeLock.unlock();
            }
        }

        public void decrement() {
            writeLock.lock();
            try {
                counter--;
            } finally {
                writeLock.unlock();
            }
        }

    }

}



...