欢迎我们关注大众号「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
    • 循环次数永久
  • 关注聚合报告指标
  • 履行方式
    • 同步履行
    • 异步履行,线程池使用CPU密布型
    • 异步履行,线程池使用IO密布型

3.2 压测剖析

3.2.1 压测成果

压测耗时表格.jpg

  • 同步履行
    • 在不同线程数下耗时总体安稳,均为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」一同交流学习