java并发编程之-JUC并发容器的使用
在 Java 5.0 提供了java.util.concurrent(简称JUC)并发编程容器包,在此包中增加了在并发编程中很常用的工具类,用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中的 Collection 实现等我们拿其中常用的list容器来作为例子,进行高压(高并发测试),使用代码和JMter测试工具来分别测试线程安全问题...
在 Java 5.0 提供了 java.util.concurrent(简称JUC)并发编程容器包,在此包中增加了在并发编程中很常用的工具类,用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中的 Collection 实现等
我们拿其中常用的list容器来作为例子,进行高压(高并发测试),使用代码和JMter测试工具来分别测试线程安全问题
demo GitHub https://github.com/zhang-xiaoxiang/gaobinfa
1:使用代码展示list容器的并发测试结果
package com.example.gaobinfa.common;
import com.example.gaobinfa.annoations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
@NotThreadSafe//这是自定义注解,标注为不安全(可以不使用)
public class ArrayListExample {
/**
* 请求总数
*/
public static int clientTotal = 5000;
/**
* 同时并发执行的线程数
*/
public static int threadTotal = 200;
private static List<Integer> list = new ArrayList<>();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal; i++) {
final int count = i;
executorService.execute(() -> {
try {
semaphore.acquire();
update(count);
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
//甚至可能抛异常 java.lang.ArrayIndexOutOfBoundsException: 33
log.info("size:{}", list.size());
}
private static void update(int i) {
list.add(i);
}
}
运行结果如下(多次测试也没有达到预期的5000)
甚至抛异常
2:使用Jmter测试工具测试(我们这里以web接口作为测试,方便使用JMter)
接口代码
package com.example.gaobinfa.api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
* CommonApi:这里模拟常用的集合类,在高并发下体现不安全的演示示例
*
* @author zhangxiaoxiang
* @date: 2019/08/11
*/
@RestController
@RequestMapping("/get")
@Slf4j
public class CommonApi {
Integer i = 0;
List<Integer> list = new ArrayList<>();
/**
* 调用一次接口对集合add操作
*
* @return
*/
@RequestMapping("/testarraylist")
public Object testArrayList() {
list.add(i);
i++;
//在没有高并发下正常请求5000次是从1到5000的
log.info("数组长度=" + list.size());
return "数组长度=" + list.size();
}
}
工具测试调试
测试结果(仍然没有达到5000,和预期不一致,线程不安全)
结论:List容器是存在安全问题的
使用并发容器改善 list对应的并发容器是CopyOnWriteArrayList
1代码测试
package com.example.gaobinfa.common.concurrent;
import com.example.gaobinfa.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.concurrent.*;
@Slf4j
@ThreadSafe
public class CopyOnWriteArrayListExample {
/**
* 请求总数
*/
public static int clientTotal = 5000;
/**
* 同时并发执行的线程数
*/
public static int threadTotal = 200;
private static List<Integer> list = new CopyOnWriteArrayList<>();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal; i++) {
final int count = i;
executorService.execute(() -> {
try {
semaphore.acquire();
update(count);
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("size:{}", list.size());
}
private static void update(int i) {
list.add(i);
}
}
运行结果(多次测试)
2JMter工具测试
package com.example.gaobinfa.api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* ConcurrentApi:测试JUC并发容器改善后的测试
*
* @author zhangxiaoxiang
* @date: 2019/08/11
*/
@RestController
@RequestMapping("/get")
@Slf4j
public class ConcurrentApi {
Integer i = 0;
List<Integer> list = new CopyOnWriteArrayList<>();
/**
* 调用一次接口对集合add操作
*
* @return
*/
@RequestMapping("/copyonwritearraylist")
public Object testArrayList() {
list.add(i);
i++;
//在没有高并发下正常请求5000次是从1到5000的
log.info("使用并发集合JUC改善后数组长度=" + list.size());
return "使用并发集合JUC改善后数组长度=" + list.size();
}
}
工具(多次)测试结果
这下和预期的一致了,线程安全了,其他像map集合等类似的操作即可,我的文件结构
更多推荐
所有评论(0)