java并发编程并发容器_java并发编程之-JUC并发容器的使用-Go语言中文社区

在 Java 5.0 提供了 java.util.concurrent(简称JUC)并发编程容器包,在此包中增加了在并发编程中很常用的工具类,用于定义类似于线程的自定义子系统,包括线程池,异步 IO 和轻量级任务框架;还提供了设计用于多线程上下文中的 Collection 实现等

我们拿其中常用的list容器来作为例子,进行高压(高并发测试),使用代码和JMter测试工具来分别测试线程安全问题

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 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)

249a2039990e8322c8e80be9109b2822.png

8352a62256969c435fe5b2804d486929.png

甚至抛异常

java并发编程并发容器_java并发编程之-JUC并发容器的使用-Go语言中文社区_第1张图片

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 list = new ArrayList<>();

/**

* 调用一次接口对集合add操作

*

* @return

*/

@RequestMapping("/testarraylist")

public Object testArrayList() {

list.add(i);

i++;

//在没有高并发下正常请求5000次是从1到5000的

log.info("数组长度=" + list.size());

return "数组长度=" + list.size();

}

}

工具测试调试

java并发编程并发容器_java并发编程之-JUC并发容器的使用-Go语言中文社区_第2张图片

java并发编程并发容器_java并发编程之-JUC并发容器的使用-Go语言中文社区_第3张图片

测试结果(仍然没有达到5000,和预期不一致,线程不安全)

0e3bb3f8e3094db6f0bf37554f27c9d6.png

结论: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 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);

}

}

运行结果(多次测试)

62dca4b971ebb51a3fbc934775b19525.png

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 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();

}

}

工具(多次)测试结果

75dba0776bf915f14bdf67a455c100cb.png

这下和预期的一致了,线程安全了,其他像map集合等类似的操作即可,我的文件结构

java并发编程并发容器_java并发编程之-JUC并发容器的使用-Go语言中文社区_第4张图片

你可能感兴趣的:(java并发编程并发容器)