我试图证明当有许多读者和只有一些作者时,synchronized会变慢.不知怎的,我证明了相反.
RW示例,执行时间为313 ms:
package zad3readWriteLockPerformance; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; 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) + " ms"); })); start = System.currentTimeMillis(); final int NUMBER_OF_THREADS = 1000; ThreadSafeArrayListthreadSafeArrayList = new ThreadSafeArrayList<>(); ArrayList consumerThreadList = new ArrayList (); for (int i = 0; i < NUMBER_OF_THREADS; i++) { Thread t = new Thread(new Consumer(threadSafeArrayList)); consumerThreadList.add(t); t.start(); } ArrayList producerThreadList = new ArrayList (); for (int i = 0; i < NUMBER_OF_THREADS/10; i++) { Thread t = new Thread(new Producer(threadSafeArrayList)); producerThreadList.add(t); t.start(); } // System.out.println("Printing the First Element : " + threadSafeArrayList.get(1)); } } class Consumer implements Runnable { public final static int NUMBER_OF_OPERATIONS = 100; ThreadSafeArrayList threadSafeArrayList; public Consumer(ThreadSafeArrayList threadSafeArrayList) { this.threadSafeArrayList = threadSafeArrayList; } @Override public void run() { for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) { Integer obtainedElement = threadSafeArrayList.getRandomElement(); } } } class Producer implements Runnable { public final static int NUMBER_OF_OPERATIONS = 100; ThreadSafeArrayList threadSafeArrayList; public Producer(ThreadSafeArrayList threadSafeArrayList) { this.threadSafeArrayList = threadSafeArrayList; } @Override public void run() { for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) { threadSafeArrayList.add((int) (Math.random() * 1000)); } } } class ThreadSafeArrayList { private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final Lock readLock = readWriteLock.readLock(); private final Lock writeLock = readWriteLock.writeLock(); private final List 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(); } }
同步示例,执行时间仅为241ms:
package zad3readWriteLockPerformanceZMIENONENENASYNCHRO; import java.util.ArrayList; import java.util.Collections; import java.util.List; 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) + " ms"); })); start = System.currentTimeMillis(); final int NUMBER_OF_THREADS = 1000; Listlist = Collections.synchronizedList(new ArrayList ()); ArrayList consumerThreadList = new ArrayList (); for (int i = 0; i < NUMBER_OF_THREADS; i++) { Thread t = new Thread(new Consumer(list)); consumerThreadList.add(t); t.start(); } ArrayList producerThreadList = new ArrayList (); for (int i = 0; i < NUMBER_OF_THREADS / 10; i++) { Thread t = new Thread(new Producer(list)); producerThreadList.add(t); t.start(); } // System.out.println("Printing the First Element : " + threadSafeArrayList.get(1)); } } class Consumer implements Runnable { public final static int NUMBER_OF_OPERATIONS = 100; List list; public Consumer(List list) { this.list = list; } @Override public void run() { for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) { if (list.size() > 0) list.get((int) (Math.random() * list.size())); } } } class Producer implements Runnable { public final static int NUMBER_OF_OPERATIONS = 100; List threadSafeArrayList; public Producer(List threadSafeArrayList) { this.threadSafeArrayList = threadSafeArrayList; } @Override public void run() { for (int j = 0; j < NUMBER_OF_OPERATIONS; j++) { threadSafeArrayList.add((int) (Math.random() * 1000)); } } }
当我的读者数量比作者多十倍时,为什么同步收集会更快.如何显示我在许多文章中读到的RW锁的进展?
获取ReadWriteLock的实际成本通常比获取简单互斥锁的成本要慢得多.ReadWriteLock 的javadoc进入:
读写锁是否会提高使用互斥锁的性能取决于与被修改相比读取数据的频率,读写操作的持续时间以及数据的争用 - 是,将尝试同时读取或写入数据的线程数.例如,最初填充数据并且之后不经常修改的集合在被频繁搜索时(例如某种目录)是使用读写锁的理想候选者.但是,如果更新变得频繁,那么数据的大部分时间都会被完全锁定,并且并发性几乎没有增加.此外,如果读取操作太短,则读写锁定实现的开销(其本质上比互斥锁更复杂)可以支配执行成本,特别是因为许多读写锁实现仍然通过序列化所有线程.小部分代码.最终,只有分析和测量才能确定使用读写锁是否适合您的应用.
因此,您的线程执行非常简单的操作这一事实可能意味着性能主要取决于实际获取锁定所花费的时间.
您的基准测试还有另一个问题,那Math.random
就是同步.从它的javadoc:
此方法已正确同步,以允许多个线程正确使用.但是,如果许多线程需要以很高的速率生成伪随机数,则可以减少每个线程拥有自己的伪随机数生成器的争用.
因此,即使您的并发读者在获取ReadWriteLock时没有相互阻塞,他们仍可能争夺获得的锁定Math.random
,从而打败了使用ReadWriteLock的一些优势.您可以通过使用ThreadLocalRandom来改进这一点.此类的用法通常应为以下形式:ThreadLocalRandom.current().nextX(...)(其中X为Int,Long等).
此外,正如assylias指出的那样,没有考虑JIT编译和其他运行时怪癖的天真Java基准测试是不可靠的.您应该使用Java Microbenchmarking Harness(JMH)来完成这些基准测试.