欢迎我们关注大众号「JAVA前哨」检查更多精彩共享文章,首要包含源码剖析、实际使用、架构思想、职场共享、产品思考等等,一同欢迎我们加我微信「java_front」一同交流学习
1 代码实例
1.1 A服务
声明A服务供给五个办法:
public class BizParamDTO {
private String field;
}
public interface AService {
public String a1(BizParamDTO param) throws Exception;
public String a2(BizParamDTO param) throws Exception;
public String a3(BizParamDTO param) throws Exception;
public String a4(BizParamDTO param) throws Exception;
public String a5(BizParamDTO param) throws Exception;
}
a1-a4休眠100毫秒,a5休眠600毫秒:
@Service
public class AServiceImpl implements AService {
@Override
public String a1(BizParamDTO param) throws Exception {
System.out.println(Thread.currentThread().getName() + ",a1 param=" + param);
TimeUnit.MILLISECONDS.sleep(100);
return param.getField();
}
@Override
public String a2(BizParamDTO param) throws Exception {
System.out.println(Thread.currentThread().getName() + ",a2 param=" + param);
TimeUnit.MILLISECONDS.sleep(100);
return param.getField();
}
@Override
public String a3(BizParamDTO param) throws Exception {
System.out.println(Thread.currentThread().getName() + ",a3 param=" + param);
TimeUnit.MILLISECONDS.sleep(100);
return param.getField();
}
@Override
public String a4(BizParamDTO param) throws Exception {
System.out.println(Thread.currentThread().getName() + ",a4 param=" + param);
TimeUnit.MILLISECONDS.sleep(100);
return param.getField();
}
@Override
public String a5(BizParamDTO param) throws Exception {
System.out.println(Thread.currentThread().getName() + ",a5 param=" + param);
TimeUnit.MILLISECONDS.sleep(600);
return param.getField();
}
}
1.2 B服务
B服务同步调用A服务五个办法:
public interface BService {
public void b(BizParamDTO param) throws Exception;
}
@Service
public class BServiceImpl implements BService {
@Resource
private AService aservice;
@Override
public void b(BizParamDTO param) throws Exception {
StopWatch stopWatch = new StopWatch();
stopWatch.start("taskB");
String r1 = aservice.a1(param);
String r2 = aservice.a2(param);
String r3 = aservice.a3(param);
String r4 = aservice.a4(param);
String r5 = aservice.a5(param);
List<String> result = Arrays.asList(r1, r2, r3, r4, r5);
stopWatch.stop();
System.out.println("b1-costTime=" + stopWatch.getTotalTimeMillis() + "ms");
}
}
1.3 C服务
C服务经过异步调用A服务:
public interface CService {
public void c(BizParamDTO param) throws Exception;
}
@Service
public class CServiceImpl implements CService {
private final static Integer TYPE = TypeEnum.CPU.getCode();
@Resource
private AService aservice;
@Override
public void c(BizParamDTO param) throws Exception {
StopWatch stopWatch = new StopWatch();
stopWatch.start("taskC");
Future<String> f1 = MyThreadFactory.get(TYPE).submit(new Callable<String>() {
@Override
public String call() throws Exception {
StopWatch s = new StopWatch();
s.start();
String result = aservice.a1(param);
s.stop();
System.out.println("a1-costTime=" + s.getTotalTimeMillis() + "ms");
return result;
}
});
Future<String> f2 = MyThreadFactory.get(TYPE).submit(new Callable<String>() {
@Override
public String call() throws Exception {
StopWatch s = new StopWatch();
s.start();
String result = aservice.a2(param);
s.stop();
System.out.println("a2-costTime=" + s.getTotalTimeMillis() + "ms");
return result;
}
});
Future<String> f3 = MyThreadFactory.get(TYPE).submit(new Callable<String>() {
@Override
public String call() throws Exception {
StopWatch s = new StopWatch();
s.start();
String result = aservice.a3(param);
s.stop();
System.out.println("a3-costTime=" + s.getTotalTimeMillis() + "ms");
return result;
}
});
Future<String> f4 = MyThreadFactory.get(TYPE).submit(new Callable<String>() {
@Override
public String call() throws Exception {
StopWatch s = new StopWatch();
s.start();
String result = aservice.a4(param);
s.stop();
System.out.println("a4-costTime=" + s.getTotalTimeMillis() + "ms");
return result;
}
});
Future<String> f5 = MyThreadFactory.get(TYPE).submit(new Callable<String>() {
@Override
public String call() throws Exception {
StopWatch s = new StopWatch();
s.start();
String result = aservice.a5(param);
s.stop();
System.out.println("a5-costTime=" + s.getTotalTimeMillis() + "ms");
return result;
}
});
// 等候成果
StopWatch watch = new StopWatch("waitWatch");
watch.start("f1.get()");
String r1 = f1.get();
watch.stop();
watch.start("f2.get()");
String r2 = f2.get();
watch.stop();
watch.start("f3.get()");
String r3 = f3.get();
watch.stop();
watch.start("f4.get()");
String r4 = f4.get();
watch.stop();
watch.start("f5.get()");
String r5 = f5.get();
watch.stop();
// 输出成果
List<String> result = Arrays.asList(r1, r2, r3, r4, r5);
stopWatch.stop();
System.out.println("c1-costTime=" + stopWatch.getTotalTimeMillis() + "ms,costTimeDetail=" + watch.prettyPrint());
}
}
1.4 线程池
我们把线程池划分为两种类型:
public enum TypeEnum {
IO(1, "IO密布"),
CPU(2, "CPU密布")
}
CPU密布型线程数:CPU数量+1
IO密布型线程数:CPU数量除以(1-阻塞系数0.9)
public class MyThreadFactory {
/** 线程履行器 **/
private static volatile ThreadPoolExecutor executor;
/** 队列存放使命数 **/
private static int QUEUE_MAX_SIZE = 1000;
/** 线程存活时刻 **/
private static long KEEP_ALIVE_TIME = 1000;
public static ThreadPoolExecutor get(int type) {
if (executor == null) {
synchronized (ThreadFactory.class) {
if (executor == null) {
int cpuNum = Runtime.getRuntime().availableProcessors();
int coreSize = cpuNum;
if (type == TypeEnum.CPU.getCode()) {
coreSize = cpuNum + 1;
} else if (type == TypeEnum.IO.getCode()) {
coreSize = cpuNum * 10;
}
int maxSize = coreSize;
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(QUEUE_MAX_SIZE);
executor = new ThreadPoolExecutor(coreSize, maxSize, KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, queue);
}
}
}
return executor;
}
/**
* 本机8核16处理器
*/
public static void main(String[] args) {
int cpuNum = Runtime.getRuntime().availableProcessors(); // 16
System.out.println("cpuNum=" + cpuNum);
}
}
1.5 拜访端点
@RestController
@RequestMapping("/test")
public class BizController {
@Resource
private BService bservice;
@Resource
private CService cservice;
@PostMapping("/biz1")
public boolean biz1(@RequestBody BizParamDTO param) throws Exception {
bservice.b(param);
return true;
}
@PostMapping("/biz2")
public boolean biz2(@RequestBody BizParamDTO param) throws Exception {
cservice.c(param);
return true;
}
}
2 单次履行
2.1 同步履行
postman拜访端点biz1,此刻挑选CPU密布型线程池:
http://localhost:8080/javafront/test/biz1
{
"field": "a"
}
耗时日志如下:
b1-costTime=1036ms
耗时计算公式:
100ms(a1) + 100ms(a2) + 100ms(a3) + 100ms(a4) + 500ms(a5) = 1000ms
2.2 异步履行
postman拜访端点biz2:
http://localhost:8080/javafront/test/biz2
{
"field": "a"
}
耗时日志如下:
a2-costTime=104ms
a4-costTime=104ms
a1-costTime=104ms
a3-costTime=104ms
a5-costTime=602ms
c1-costTime=604ms,costTimeDetail=StopWatch 'waitWatch': running time = 604224000 ns
---------------------------------------------
ns % Task name
---------------------------------------------
105678000 017% f1.get()
000001800 000% f2.get()
000048100 000% f3.get()
000000400 000% f4.get()
498495700 083% f5.get()
本次耗时日志稍显复杂,能够把日志分为履行部分和等候部分:
- 履行部分
- a1-a4别离履行耗时104ms
- a5履行耗时602ms
a2-costTime=104ms
a4-costTime=104ms
a1-costTime=104ms
a3-costTime=104ms
a5-costTime=602ms
- 等候部分
- c1总共耗时604ms
- f1等候时刻105ms
- f2-f4等候时刻为纳秒级
- f5等候时刻498ms
c1-costTime=604ms,costTimeDetail=StopWatch 'waitWatch': running time = 604224000 ns
---------------------------------------------
ns % Task name
---------------------------------------------
105678000 017% f1.get()
000001800 000% f2.get()
000048100 000% f3.get()
000000400 000% f4.get()
498495700 083% f5.get()
2.3 本章总结
- 同步履行耗时为每个节点耗时累加
- 异步履行耗时为节点中耗时最长节点
- 单次履行耗时异步优于同步
3 压力测试
3.1 压测思路
- 使用工具JMeter
- 线程组装备
- 线程数10、20、30递增
- Ramp-Up时刻0秒
- 持续时刻60s
- 循环次数永久
- 关注聚合报告指标
- 95Line
- 吞吐量
- 异常比例
- 履行方式
- 同步履行
- 异步履行,线程池使用CPU密布型
- 异步履行,线程池使用IO密布型
3.2 压测剖析
3.2.1 压测成果
- 同步履行
- 在不同线程数下耗时总体安稳,均为1000ms左右
- 异步(IO密布型)
- 在不同线程数下耗时总体安稳,均为600ms左右
- 异步(CPU密布型)
- 随着线程数增多,耗时越来越大,功能体现不如同步
3.2.2 耗时剖析
现在剖析异步在50线程时耗时日志,剖析耗时首要发生履行部分,还是发生在等候部分。
(1) 异步(IO密布型)
a5-costTime=601ms
a1-costTime=108ms
a4-costTime=108ms
a2-costTime=108ms
a3-costTime=108ms
c1-costTime=602ms,costTimeDetail=StopWatch 'waitWatch': running time = 602183001 ns
---------------------------------------------
ns % Task name
---------------------------------------------
105515000 018% f1.get()
000000200 000% f2.get()
000777201 000% f3.get()
000101800 000% f4.get()
495788800 082% f5.get()
- 履行部分
- a1-a4别离履行耗时约为108ms
- a5履行耗时601ms
- 等候部分
- f1等候时刻约为150ms
- f5等候时刻约为495ms
- 剖析小结
- 履行和等候均符合预期
(2) 异步(CPU密布型)
a1-costTime=110ms
a2-costTime=109ms
a3-costTime=110ms
a5-costTime=613ms
a4-costTime=110ms
c1-costTime=3080ms,costTimeDetail=StopWatch 'waitWatch': running time = 3080277201 ns
---------------------------------------------
ns % Task name
---------------------------------------------
2528728000 082% f1.get()
016059001 001% f2.get()
031992801 001% f3.get()
000046199 000% f4.get()
503451200 016% f5.get()
-
履行部分
- a1-a4别离履行耗时约为110ms
- a5履行耗时613ms
-
等候部分
- f1等候时刻约为2.5s
- f5等候时刻约为500ms
- 其它使命等候时刻也有所增加
-
剖析小结
- 耗时首要在等候部分
- 履行部分耗时符合预期
3.3 压测总结
- 假如线程池挑选不合适,异步功能不如同步
- 假如使命耗时长,应该增加装备线程数
4 文章总结
本文榜首编写了同步和异步代码,并在代码中输出了耗时日志。第二剖析单次履行同步和异步的体现,异步优于同步。第三结合不同线程池装备进行压测,假如线程池挑选不合适,异步履行功能不如同步,所以要装备合适线程数。
欢迎我们关注大众号「JAVA前哨」检查更多精彩共享文章,首要包含源码剖析、实际使用、架构思想、职场共享、产品思考等等,一同欢迎我们加我微信「java_front」一同交流学习