![](https://img.haomeiwen.com/i17044733/ca17ae50789ee1ab.jpeg)
此前有个汇聚功能的响应时间特别长,走查代码发现以前版本完全用单线程来实现的,我将这个功能简单的抽象了一下,代码实现如下所示:
public class ListThreadSafeTest {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
for(int j = 0; j < 800; j++) {
//do something
list.add("aa");
}
System.out.println(list.size());
}
}
采用单线程的方式,业务逻辑为cpu密集型的,当数据量比较大的时候,采用单线程for循环的方式处理,性能可想而知。后来我用CountDownLatch对代码进行了改造,如下所示
public class ListThreadSafeTest {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
//测试代码,这里就不追求细节了
Executor executor = Executors.newFixedThreadPool(8);
CountDownLatch latch = new CountDownLatch(8);
for(int i = 0; i < 8 ; i++) {
executor.execute(() -> {
List<String> list1 = new ArrayList<>();
for(int j = 0; j < 100; j++) {
list.add("aa");
}
latch.countDown();
});
}
latch.await();
System.out.println(list.size());
}
}
测试性能提升十分明显,但是上线之后,该功能出现了大量数据不准确的情况,即list.size()的值不是800,而是随机出现一个小于800的结果,后来排查问题的时候想到,ArrayList不是线程安全的集合,在多线程并发写的过程中会出现线程安全问题,为了解决线程安全问题引入了一个线程安全的集合类ConcurrentHashMap来保存中间结果,问题解决。后来我用HashMap代替ConcurrentHashMap试了一下,同样会出现问题,因为HashMap也是非线程安全的。
public class ListThreadSafeTest {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
//Map<String, List<String>> map = new HashMap<>();
Map<String, List<String>> map = new ConcurrentHashMap<>();
//测试代码,这里就不追求细节了
Executor executor = Executors.newFixedThreadPool(8);
CountDownLatch latch = new CountDownLatch(8);
for(int i = 0; i < 8 ; i++) {
executor.execute(() -> {
List<String> list1 = new ArrayList<>();
for(int j = 0; j < 100; j++) {
list1.add("aa");
}
map.put(UUID.randomUUID().toString(), list1);
latch.countDown();
});
}
latch.await();
map.forEach((key, value) -> {
list.addAll(value);
});
System.out.println(list.size());
}
}
平常把高并发和线程安全挂在嘴边,真正到运用的时候却出现这种比较低级的错误,看来学知识还是不能仅限于表面,学以致用才是王道。
网友评论