1

我创建了 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();
            }
        }

    }

}
4

2 回答 2

4

这些类型的锁——读写——通常经过优化,适合许多读者和一个或几个作者。他们经常在 ops 上旋转,期望读取速度快而写入量很少。此外,它们针对公平性或请求的 FIFO 处理进行了优化,以避免线程停止。

你做的恰恰相反。您编写了许多写入器,这会导致过度旋转和其他复杂的方法,适用于多读少写场景。

简单的锁很简单。他们只是在准备好时阻塞所有线程,并且不会发生旋转。它们的缺点是当它们唤醒多个线程让它们再次休眠时会引起雪崩效应。

于 2019-10-20T09:28:41.860 回答
0

感谢尼克和斯劳指出,它不是阅读主导。我确保我有 100 个增量、100 个减量和 1000 个读取线程。

结果如期而至。ReentrantReadWriteLock 的输出为 300 ms,withLock 的输出为 5000 ms。

这是修改后的代码

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

    }

}



于 2019-10-20T11:36:37.453 回答