What I am curious about whether we can do it the way below?
Yes, you can.
If yes, any reference?
You can do synchronization on any Object
s. since ArrayList
is an Object
so you can do it.
you should declare clients
as a final
variable, as different threads may be locking on different objects even when operating on same object.
Also, You can use java.util.concurrent.locks.Lock
and/or java.util.concurrent.locks.ReadWriteLock
instead of synchronized
block or simply The java.util.concurrent.CopyOnWriteArrayList
that meets your needs.
I've done a simple performance test :
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ConcurrentReadWriteListTest {
public static void main(String[] args) throws InterruptedException {
final int iterations = 10;
final long durationMillis = 5000;
System.out.println("WarmUp :");
System.out.println("\n************* SynchronizedArrayList ************");
testSynchronizedArrayList(5, 1000);
System.out.println("\n************* CopyOnWriteArrayList *************");
testCopyOnWriteArrayList(5, 1000);
System.out.println("\n********** ArrayListWithReadWriteLock **********");
testArrayListWithReadWriteLock(5, 1000);
System.out.println("\n\nStart Test :");
System.out.println("\n************* SynchronizedArrayList ************");
testSynchronizedArrayList(iterations, durationMillis);
System.out.println("\n************* CopyOnWriteArrayList *************");
testCopyOnWriteArrayList(iterations, durationMillis);
System.out.println("\n********** ArrayListWithReadWriteLock **********");
testArrayListWithReadWriteLock(iterations, durationMillis);
}
static void testSynchronizedArrayList(int iterations, long durationMillis) {
final List<TestObject> list = Collections.synchronizedList(new ArrayList<>());
final AtomicInteger writes = new AtomicInteger();
final AtomicInteger reads = new AtomicInteger();
final Runnable w = () -> {
while (notInterrupted()) {
list.add(new TestObject());
writes.getAndIncrement();
}
};
final Runnable r = () -> {
while (notInterrupted()) {
if (!list.isEmpty()) {
list.get(ThreadLocalRandom.current().nextInt(list.size())).map.size();
reads.getAndIncrement();
}
}
};
beginTest(iterations, durationMillis, list, writes, reads, w, r);
}
static void testCopyOnWriteArrayList(int iterations, long durationMillis) {
final List<TestObject> list = new CopyOnWriteArrayList<>();
final AtomicInteger writes = new AtomicInteger();
final AtomicInteger reads = new AtomicInteger();
final Runnable w = () -> {
while (notInterrupted()) {
list.add(new TestObject());
writes.getAndIncrement();
}
};
final Runnable r = () -> {
while (notInterrupted()) {
if (!list.isEmpty()) {
list.get(ThreadLocalRandom.current().nextInt(list.size())).map.size();
reads.getAndIncrement();
}
}
};
beginTest(iterations, durationMillis, list, writes, reads, w, r);
}
static void testArrayListWithReadWriteLock(int iterations, long durationMillis) {
final List<TestObject> list = new ArrayList<>();
final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
final AtomicInteger writes = new AtomicInteger();
final AtomicInteger reads = new AtomicInteger();
final Runnable w = () -> {
while (notInterrupted()) {
readWriteLock.writeLock().lock();
list.add(new TestObject());
readWriteLock.writeLock().unlock();
writes.getAndIncrement();
}
};
final Runnable r = () -> {
while (notInterrupted()) {
readWriteLock.readLock().lock();
if (!list.isEmpty()) {
list.get(ThreadLocalRandom.current().nextInt(list.size())).map.size();
reads.getAndIncrement();
}
readWriteLock.readLock().unlock();
}
};
beginTest(iterations, durationMillis, list, writes, reads, w, r);
}
static void beginTest(int iterations, long durationMillis,
List<TestObject> list,
AtomicInteger writes, AtomicInteger reads,
Runnable w, Runnable r) {
double totalWPS = 0, totalRPS = 0;
for (int i = 0; i < iterations; i++) {
Thread writer = new Thread(w);
Thread reader = new Thread(r);
writer.start();
reader.start();
sleep(durationMillis);
writer.interrupt();
reader.interrupt();
double wps = writes.get() * 1.0 / durationMillis * 1000;
double rps = reads.get() * 1.0 / durationMillis * 1000;
totalWPS += wps;
totalRPS += rps;
System.out.printf("round(%d) : %.2f w/s, %.2f r/s%n", i + 1, wps, rps);
list.clear();
writes.set(0);
reads.set(0);
}
System.out.printf("overall : %.2f w/s, %.2f r/s%n", totalWPS / iterations, totalRPS / iterations);
}
static boolean notInterrupted() {
return !Thread.currentThread().isInterrupted();
}
static void sleep(long timeout) {
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static final class TestObject {
int i;
long l;
float f;
double d;
Map<Long, String> map = new HashMap<>();
TestObject() {
map.put(1L, "a");
map.put(2L, "b");
map.put(3L, "c");
map.put(4L, "d");
}
}
}
here's the results :
WarmUp :
************* SynchronizedArrayList ************
round(1) : 417859.00 w/s, 361950.00 r/s
round(2) : 845857.00 w/s, 796326.00 r/s
round(3) : 1444301.00 w/s, 1731925.00 r/s
round(4) : 1170101.00 w/s, 954809.00 r/s
round(5) : 1373893.00 w/s, 1343517.00 r/s
overall : 1050402.20 w/s, 1037705.40 r/s
************* CopyOnWriteArrayList *************
round(1) : 52490.00 w/s, 12505323.00 r/s
round(2) : 54170.00 w/s, 12963819.00 r/s
round(3) : 55552.00 w/s, 12723022.00 r/s
round(4) : 56634.00 w/s, 12963812.00 r/s
round(5) : 57261.00 w/s, 12859154.00 r/s
overall : 55221.40 w/s, 12803026.00 r/s
********** ArrayListWithReadWriteLock **********
round(1) : 1526100.00 w/s, 47806.00 r/s
round(2) : 2028127.00 w/s, 97690.00 r/s
round(3) : 2277951.00 w/s, 129597.00 r/s
round(4) : 2065762.00 w/s, 178288.00 r/s
round(5) : 1815661.00 w/s, 200642.00 r/s
overall : 1942720.20 w/s, 130804.60 r/s
Start Test :
************* SynchronizedArrayList ************
round(1) : 766045.40 w/s, 670402.20 r/s
round(2) : 943151.00 w/s, 596922.60 r/s
round(3) : 799154.60 w/s, 603495.60 r/s
round(4) : 799162.40 w/s, 633526.40 r/s
round(5) : 799159.80 w/s, 595249.40 r/s
round(6) : 798720.00 w/s, 526994.40 r/s
round(7) : 959635.00 w/s, 692030.20 r/s
round(8) : 959631.60 w/s, 651675.00 r/s
round(9) : 959632.40 w/s, 652545.80 r/s
round(10) : 959632.40 w/s, 689399.60 r/s
overall : 874392.46 w/s, 631224.12 r/s
************* CopyOnWriteArrayList *************
round(1) : 23536.60 w/s, 13772252.60 r/s
round(2) : 24099.40 w/s, 13332508.00 r/s
round(3) : 24754.80 w/s, 12908797.20 r/s
round(4) : 24895.80 w/s, 12503731.20 r/s
round(5) : 24799.40 w/s, 12386501.60 r/s
round(6) : 24966.20 w/s, 12297215.40 r/s
round(7) : 24769.00 w/s, 12245010.20 r/s
round(8) : 24958.80 w/s, 12399174.00 r/s
round(9) : 24748.40 w/s, 12353819.80 r/s
round(10) : 24942.80 w/s, 12400986.80 r/s
overall : 24647.12 w/s, 12659999.68 r/s
********** ArrayListWithReadWriteLock **********
round(1) : 816296.80 w/s, 67670.40 r/s
round(2) : 872701.20 w/s, 81845.20 r/s
round(3) : 936945.20 w/s, 72189.40 r/s
round(4) : 936311.00 w/s, 80164.40 r/s
round(5) : 934575.00 w/s, 80447.80 r/s
round(6) : 932832.60 w/s, 95923.00 r/s
round(7) : 935632.20 w/s, 85815.00 r/s
round(8) : 935526.20 w/s, 85754.40 r/s
round(9) : 930819.20 w/s, 92297.60 r/s
round(10) : 934331.20 w/s, 94485.60 r/s
overall : 916597.06 w/s, 83659.28 r/s
- JVM : Java HotSpot(TM) 64-Bit Server VM (build 25.72-b15, mixed mode)
- OS : Win 10 Pro 64-bit
- CPU : Intel Core i7-4700HQ @ 2.40GHz 2.40GHz
so, if you want more and better
- Concurrent Reads, use
CopyOnWriteArrayList
- Concurrent Writes, use
ArrayList
with ReadWriteLock
and use synchronized
block if you want something in between.