Java8 and Vector - yes, you can use it again!

veröffentlicht am : Mo, 23. 11. 2015 geändert am: Di, 16. 05. 2017

Kategorie: Computer

Schlagworte:


I collegue of mine came to me today and mentionend, that the use of ArrayList would cause problems in multithreadded environments - and he's right! At this very occasion it is discussing some internal cache of our application, where a lacking object here and there is not ab big deal. BUT: What we found out with his help is the following:

We were experimenting with lock and synchronized a bit, and found, that locks are way slower than using synchronized - in java 8 that is. There seems to be siginficant performance optimization in the synchonization in the VM itself. So, we wanted to compare the access to a list in a multithreadded environment and measure the timings. Here is the method, we used:

 private void testIt(final List lst) {
    long start = System.currentTimeMillis();
    int threads = 300;
    threadCount = 0;
    for (int i = 0; i < threads; i++) {
        final int j = i;
        new Thread() {
            public void run() {
                for (int k = 0; k < 1000; k++) {
//                        synchronized (lst) {
                    try {
                        lst.add("hello " + j + " - " + k);
                    } catch (Exception e) {
                        //ignore
                    }
//                        }
                }
                threadCount++;
            }
        }.start();
    }

    while (threadCount < threads) {
        Thread.yield();
    }
    long dur = System.currentTimeMillis() - start;
    System.out.println("write took : " + dur);
    System.out.println("Counting   : " + lst.size() + " missing: " + ((threads * 1000) - lst.size()));
    threadCount = 0;
    start = System.currentTimeMillis();
    for (int i = 0; i < threads; i++) {
        final int j = i;
        new Thread() {
            public void run() {
                for (int k = 0; k < 1000; k++) {
//                        synchronized (lst) {
                    try {
                        if (j * 1000 + k < lst.size())
                            lst.get(j * 1000 + k);
                    } catch (Exception e) {
                        //ignore
                    }
//                        }
                }
                threadCount++;
            }
        }.start();
    }

    while (threadCount < threads) {
        Thread.yield();
    }
    dur = System.currentTimeMillis() - start;
    System.out.println("read took : " + dur);
}

The code does not do much: creates 300 Threads, each of those storing data into a shared List of certain type. And after that, we create 300 threads reading those values (if they are there, that is - when using non-threadsafe datastructures, you will end up with data missing!).

Here is the result:

Testing with ArraList
write took : 83
Counting   : 255210 missing: 44790
read took : 22

Testing with Vector
write took : 64
Counting   : 300000 missing: 0
read took : 89

Testing with LinkedList
write took : 38
Counting   : 249998 missing: 50002
read took : 13367

Everybody knows, it is not a good idea to use Vector - it’s old and sluggish, slow and not useful. Do your own synchronization... This has been true obvously till JDK 1.7 - We ran the same test with JDK1.7 and Vector was at least 3 as slow as ArrayList or Linkedlist (only faster in reading).

We were shocked to see, that Vector ist actually faster than ArrayList! Significantly! And Thread-Safe! And it is even faster than using the same code with a synchronized block when accessing the list (see the commented out synchronized statements in the code above):

Testing with ArraList (synchronized block)
write took : 191
Counting   : 300000 missing: 0
read took : 80

Testing with Vector
write took : 68
Counting   : 300000 missing: 0
read took : 79

Testing with LinkedList (synchronized block)
write took : 178
Counting   : 300000 missing: 0

Of course, this is not a total in depth analysis as we actually don’t know for sure, what is causing this performance increase. But it really is reassuring - love to see, that Vector got some love a gain ;-) So - in an Java8 environment, you could actually use Vector without having to think about performance issues...

Update: I just compared the creation times (Default constructor) of the different types also, these are the timings:

Duration vector    : 31ms
Duration ArrayList : 2ms
Duration LinkedList: 3ms

So, what remains is: use Vector, if you do not create too many instances of it 😉

2nd Update: I just want to make things about this test a bit more clear. People tend to tell me that "this is no proper test, no Warmup phase, no proper Threadding... yadda yadda".

you might be surprised, YES I KNOW!

Instead of discussing the Idea, they discuss the toolset... facepalm my fault. Thought, this was clear from the beginning. Sorry for that.

This piece does not try to be the proof of anything. It is just showing, that there is some significant performance increase on Java 8 vs java 7 when it comes to Vector. Also, as already mentioned above, this code was not created like this, it is just a "byproduct".

The rest of this was to put in in perspective. Agreed, this was not very clear. It shows, that when your data structure is not synchornized, you might end up with data being lost. The test quantifies this loss with numbers. Which is also interesting - but for a different topic.

The results of this piece of code are reproduceable. Which means, that the numbers might differ, but comparing everything, the numbers are quite in the same area. Again, this is not a proper micro benchmark! This is better solved with something else, I agree.

So the goal was never to prove something, it is only a hint, that even Vector might be worth trying. It is still around, right? not marked deprecated, and not used as it is "slow". This is maybe not true to the extend it used to be.

But: to make things clear. As it seems in further Tests (those were done with the JMH-Testing framework), that often the Collections.syncrhonizedList(new ArrayList<>()) returns a better performing version than Vector.

But again: this whole thing here just wants to show that the huge performance loss you got when using Vector in JDK1.7 and before is now a bit smaller... and in some cases even gone!

erstellt Stephan Bösebeck (stephan)