0

I know there is one way to synchronize the variables in Java. That is using the lock.

public class MsLunch {
    private ArrayList<ClientHandler> clients = new ArrayList<ClientHandler>();

    private Object lock1 = new Object();

    public void inc1() {
        synchronized(lock1) {
            //maybe add something into the array list
        }
    }
}

What I am curious about whether we can do it the way below? If yes, any reference?

public class MsLunch {
    private ArrayList<ClientHandler> clients = new ArrayList<ClientHandler>();

    public void inc1() {
        synchronized (clients) {
            //maybe add something into the array list
        }
    }
}
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
HuangJie
  • 1,488
  • 1
  • 16
  • 33
  • 1
    You can't synchronize variables. You can only synchronize code blocks. The value held by the object you synchronize on is unaffected. So while you can use an `ArrayList` as your monitor object, there's nothing to gain from it. – biziclop Jan 27 '16 at 16:37
  • An ArrayList is an Object, so I'm not sure what you are asking – OneCricketeer Jan 27 '16 at 16:37
  • As long as `clients` isn't null. Mark the variable as `final`, preferably. – bdkosher Jan 27 '16 at 16:43
  • One danger of this approach is that you may accidentally expose the `ArrayList` to other code (through a getter for example), and then they can synchronize on it too and lock up your code. While if you're using a dedicated `Object` for locking, that danger is much lower, as a plain object is of no use other than for locking. – biziclop Jan 27 '16 at 16:45

3 Answers3

0

There's no problem if you use clients to synchronize a block of code. You can use any object reference to synchronize a block of code. Just make sure this object won't be re initialized inside the same instance across several threads.

If you need to have a container that will be accessed through threads I would recommend reviewing Queue and Map first since usually they can solve lots of use cases. In case you need a List only, I would advice using Collections#synchronized(yourList), and as a last resource, a CopyOnWriteArrayList. Still, any of these approaches are way better than synchronizing these resources on your own.

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
0

Yes you can but you should not... Java has concurrent List utilities in java.util.concurrent package.Use CopyOnWriteArrayList Instead of using synchronize keyword performance wise its better to use a concurrent utility.That will save you from the trouble of handling locks in your code.

Dangling Piyush
  • 3,658
  • 8
  • 37
  • 52
  • 1
    I would not recommend using `CopyOnWriteArrayList` because it's very unoptimal since it will block the thread, copy all the contents of the array, add the element, then unblock the thread. This has an impact when the list has several elements and it's used acros lots of threads. In such cases, I would recommend using a `Queue` rather than a `List`, or a `Map` (using one of their synchronized implementations available in that package tho) – Luiggi Mendoza Jan 27 '16 at 16:40
  • @LuiggiMendoza Blocking the whole list for every operation is more costlier. – Dangling Piyush Jan 27 '16 at 16:41
  • Let's just agree that the uses of `CopyOnWriteArrayList` are very limited. But when it is useful, it's pretty damn useful. – biziclop Jan 27 '16 at 16:43
  • @biziclop I would use it only if there's nothing else available, and I really mean it :P and hopefully I haven't used it (yet) – Luiggi Mendoza Jan 27 '16 at 16:44
  • @user3767784 How is it more elegant than using one of the `java.util.concurrent` data structures? – user1231232141214124 Jan 27 '16 at 16:46
  • 1
    @user3767784 Depends on the situation. In and of itself there's nothing inelegant in using COWAL. Just make sure that those writes are indeed very rare. But when you need something more complex, like updating in bulk/bursts, you're better off with your own locking. – biziclop Jan 27 '16 at 16:53
0

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.

FaNaJ
  • 1,329
  • 1
  • 16
  • 39