我试图证明当有很多读者而只有一些作者时同步会更慢。不知怎的,我证明了相反的情况。
以 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;
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/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<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();
}
}
}
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));
}
}
}
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();
}
}
同步的例子,执行时间只有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;
List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
ArrayList<Thread> consumerThreadList = new ArrayList<Thread>();
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
Thread t = new Thread(new Consumer(list));
consumerThreadList.add(t);
t.start();
}
ArrayList<Thread> producerThreadList = new ArrayList<Thread>();
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<Integer> list;
public Consumer(List<Integer> 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<Integer> threadSafeArrayList;
public Producer(List<Integer> 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 的实际成本通常比获取简单互斥锁的成本慢得多。这javadoc https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReadWriteLock.html对于 ReadWriteLock 来说:
读写锁是否会比使用互斥锁提高性能取决于数据读取与修改的频率、读写操作的持续时间以及数据的争用 - 即是,同时尝试读取或写入数据的线程数。例如,最初填充了数据,此后很少修改但经常搜索的集合(例如某种目录)是使用读写锁的理想候选者。然而,如果更新变得频繁,那么数据大部分时间都被独占锁定,并且并发性几乎没有增加。此外,如果读取操作太短,读写锁实现的开销(本质上比互斥锁更复杂)可能会主导执行成本,特别是因为许多读写锁实现仍然通过一个序列化所有线程。小部分代码。最终,只有分析和测量才能确定读写锁的使用是否适合您的应用程序。
因此,您的线程正在执行非常简单的操作,这一事实可能意味着性能取决于实际获取锁所花费的时间量。
你的基准也有一个问题,那就是Math.random
已同步。从其javadoc https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html#random--:
该方法经过正确同步,以允许多个线程正确使用。但是,如果许多线程需要以很高的速率生成伪随机数,则可能会减少每个线程拥有自己的伪随机数生成器的争用。
因此,即使并发读取者在获得 ReadWriteLock 后不会互相阻塞,他们仍可能会争夺在Math.random
,抵消了使用 ReadWriteLock 的一些优点。您可以通过使用来改进这一点线程局部随机 https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadLocalRandom.html.
此外,正如 assylias 指出的那样,不考虑 JIT 编译和其他运行时怪癖的简单 Java 基准测试是不可靠的。您应该使用Java 微基准测试工具 (JMH) http://openjdk.java.net/projects/code-tools/jmh/对于这样的基准。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)