ConcurrentSkipListMap是Doug Lea在java6中新加入的一个并发集合,下面这个例子主要是测试ConcurrentSkipListMap的插入、读取和并发修改集合元素时的性能特征,代码如下:
package test.caipiao.log;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.caipiao.util.io.FileUtils;
public class ConcurrentSkipListMapTest2 {
public static void main(String[] args) throws InterruptedException, Exception {
final ConcurrentNavigableMap<Integer, String> cslm = new ConcurrentSkipListMap<Integer, String>();
final Counter5 counter = new Counter5();
// create 100 threads
ArrayList<MyThread5> threads = new ArrayList<MyThread5>();
for (int x = 0; x < 100; x++) {
threads.add(new MyThread5(counter, cslm));
}
long start1 = System.currentTimeMillis();
// start all of the threads
Iterator i1 = threads.iterator();
while (i1.hasNext()) {
MyThread5 mt = (MyThread5) i1.next();
mt.start();
}
// wait for all the threads to finish
Iterator i2 = threads.iterator();
while (i2.hasNext()) {
MyThread5 mt = (MyThread5) i2.next();
mt.join();
}
long end1 = System.currentTimeMillis();
System.out.println("100个线程 每个线程插入100000共1000万条记录,耗时: " + (end1 - start1) + " 毫秒");
System.out.println("Count: " + counter.getCount());
System.out.println("original size = " + cslm.size());
System.out.println("---------------------以上是测试插入---------------------------------------");
// create 1000 threads
ArrayList<MyThread7> threads3 = new ArrayList<MyThread7>();
for (int x = 0; x < 1000; x++) {
threads3.add(new MyThread7(counter, cslm));
}
long start3 = System.currentTimeMillis();
// start all of the threads
Iterator i3 = threads3.iterator();
while (i3.hasNext()) {
MyThread7 mt = (MyThread7) i3.next();
mt.start();
}
// wait for all the threads to finish
Iterator i33 = threads3.iterator();
while (i33.hasNext()) {
MyThread7 mt = (MyThread7) i33.next();
mt.join();
}
long end3 = System.currentTimeMillis();
System.out.println("1000个线程 每个线程插入10000共1000万条记录,耗时: " + (end3 - start3) + " 毫秒");
System.out.println("---------------------以上是测试读取---------------------------------------");
long start2 = System.currentTimeMillis();
ArrayList<MyThread6> threads2 = new ArrayList<MyThread6>();
for (int x = 0; x < 5; x++) {
threads2.add(new MyThread6(counter, cslm));
}
// start all of the threads
Iterator i12 = threads2.iterator(); //并发修改 访问 ConcurrentSkipListMap
while (i12.hasNext()) {
MyThread6 mt = (MyThread6) i12.next();
mt.start();
}
Thread t = new Thread() {
public void run() {
for (int x = 0; x < 100000; x++) {
String s = cslm.get(x);
if (x % 20 == 0) {
// System.out.println("key ---" + x + " s--- " + s);
}
}
}
};
t.start();
// wait for all the threads to finish
Iterator i22 = threads2.iterator();
while (i22.hasNext()) {
MyThread6 mt = (MyThread6) i22.next();
mt.join();
}
t.join();
long end2 = System.currentTimeMillis();
System.out.println(end2 - start2);
System.out.println("over");
System.out.println("new size = " + cslm.size());
System.out.println("---------------------以上是测试并发的修改ConcurrentSkipListMap里的元素---------------------------------------");
}
}
// thread that increments the counter 100000 times.
class MyThread5 extends Thread {
Counter5 counter;
ConcurrentNavigableMap<Integer, String> cslm;
MyThread5(Counter5 counter, ConcurrentNavigableMap<Integer, String> cslm) {
this.counter = counter;
this.cslm = cslm;
}
public void run() {
for (int x = 0; x < 100000; x++) {
int i = counter.incrementCount();
cslm.put(i, "");
}
}
}
// class that uses AtomicInteger for counter
class Counter5 {
private AtomicInteger count = new AtomicInteger(0);
public int incrementCount() {
return count.incrementAndGet();
}
public int decrementAndGet() {
return count.decrementAndGet();
}
public int getCount() {
return count.get();
}
}
//class that uses to delete part data of the cslm
class MyThread6 extends Thread {
Counter5 counter;
ConcurrentNavigableMap<Integer, String> cslm;
MyThread6(Counter5 counter, ConcurrentNavigableMap<Integer, String> cslm) {
this.counter = counter;
this.cslm = cslm;
}
public void run() {
for (int x = 0; x < 100000; x++) {
cslm.remove(x);
try {
if (x % 100 == 0) {
this.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//thread that decrements the counter 100000 times.
class MyThread7 extends Thread {
Counter5 counter;
ConcurrentNavigableMap<Integer, String> cslm;
MyThread7(Counter5 counter, ConcurrentNavigableMap<Integer, String> cslm) {
this.counter = counter;
this.cslm = cslm;
}
public void run() {
for (int x = 0; x < 10000; x++) {
int i = counter.decrementAndGet();
cslm.get(i);
}
}
}
CPU配置为4核 intel i3-2100 3.1GHz,内存分配1G,多次运行输出大致如下:
100个线程 每个线程插入100000共1000万条记录,耗时: 4719 毫秒
Count: 10000000
original size = 10000000
---------------------以上是测试插入---------------------------------------
1000个线程 每个线程插入10000共1000万条记录,耗时: 2684 毫秒
---------------------以上是测试读取---------------------------------------
1025
over
new size = 9900001
-------------------以上是测试并发的修改、访问ConcurrentSkipListMap里的元素------------------------------------
可以看到put操作的并发能达到200万/秒以上,get操作的并发能达到350万/秒以上,
这里的性能损耗有一部分是在AtomicInteger原子的递增和递减上,如果在真实的业务场景中,可以知道key的操作不需要使用AtomicInteger或加锁方式来获取,性能会更高。
通过第三个输出可以知道对ConcurrentSkipListMap访问的同时进行删除不会出现ConcurrentModificationException异常。
此例子可以把ConcurrentSkipListMap换成ConcurrentHashMap等其它集合进行测试。
如果把threads的线程数创建为10000个而每个线程执行1000次,是可以正常输出的。
把ConcurrentSkipListMap换成ConcurrentHashMap,在把threads的线程数创建为10000个而每个线程执行1000次,在线程数执行不到5000的时候会报java.lang.OutOfMemoryError: unable to create new native thread,但把每个线程的执行次数改为100的时候,可以正常的执行下去。在ConcurrentHashMap的put操作中有tryLock()这个操作会涉及到线程操作,而在ConcurrentSkipListMap的put操作中没有涉及线程操作,具体原因只有仔细的看看源码才能解释。