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 Objects. 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.