java实现异步非阻塞的几种方式-异步调用

Java8 异步非阻塞做法:CompletableFuture 两万字详解 CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。 阅读详情

java实现异步非阻塞的几种方式-异步调用

异步调用,就相关于将原来同步调用的代码块放置到一个又一个的线程中去执行,这样就可以并行的执行相关的任务。也就不用卡在一个点的等待,进而影响后续的任务。还以是订单为示例,演示整个调用的过程。

2. 异步调用

在这里插入图片描述

使用异步调用与同步调用最大的不同点,在于加入了线程池,由于任务是没有先后顺序的,可将用户查询的请和商品查询的请求都加入到线程池中,使用两个线程等待结果即可。

2.1 样例代码

首先加入的就是线程池

public class ScheduleTaskThreadPool {

  /** 进行任务调度的线程池对象 */
  public static final ScheduleTaskThreadPool INSTANCE = new ScheduleTaskThreadPool();

  /**
   * 线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;
   * 如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;
   * 如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。
   */
  private static final int MIN_THREAD_NUM = 2;

  /**
   * 线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,
   *
   * <p>前提是当前线程数小于maximumPoolSize
   */
  private static final int MAX_THREAD_NUM = 4;

  /**
   * 线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,
   *
   * <p>前提是当前线程数小于maximumPoolSize
   */
  private static final int WAIT_NUM = 8;

  /**
   * 线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间。以秒为单位
   *
   * <p>默认情况下,该参数只在线程数大于corePoolSize时才有用
   */
  private static final int KEEPALIVE = 5;

  /**
   * workQueue必须是BlockingQueue阻塞队列。当线程池中的线程数超过它的corePoolSize的时候,
   *
   * <p>线程会进入阻塞队列进行阻塞等待。 通过workQueue,线程池实现了阻塞功能 1,(1)不排队,直接提交 将任务直接交给线程处理而不保持它们,可使用SynchronousQueue
   * 如果不存在可用于立即运行任务的线程(即线程池中的线程都在工作),则试图把任务加入缓冲队列将会失败,
   * 因此会构造一个新的线程来处理新添加的任务,并将其加入到线程池中(corePoolSize-->maximumPoolSize扩容)
   * Executors.newCachedThreadPool()采用的便是这种策略
   *
   * <p>(2)无界队列 可以使用LinkedBlockingQueue(基于链表的有界队列,FIFO),理论上是该队列可以对无限多的任务排队
   * 将导致在所有corePoolSize线程都工作的情况下将新任务加入到队列中。这样, 创建的线程就不会超过corePoolSize,也因此,maximumPoolSize的值也就无效了
   *
   * <p>(3)有界队列 可以使用ArrayBlockingQueue(基于数组结构的有界队列,FIFO),并指定队列的最大长度
   * 使用有界队列可以防止资源耗尽,但也会造成超过队列大小和maximumPoolSize后,提交的任务被拒绝的问题,
   *
   * <p>比较难调整和控制。
   *
   * <p>等待任务的队列
   */
  private ArrayBlockingQueue queue = new ArrayBlockingQueue(WAIT_NUM);

  /**
   * 策略说明 1,ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
   *
   * <p>2,ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
   *
   * <p>3,ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
   *
   * <p>4,ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务,这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功。
   */
  private ThreadPoolExecutor pool =
      new ThreadPoolExecutor(
          MIN_THREAD_NUM,
          MAX_THREAD_NUM,
          KEEPALIVE,
          TimeUnit.SECONDS,
          queue,
          new ThreadFactoryBuilder().setNameFormat("order-%d").setDaemon(true).build(),
          new ThreadPoolExecutor.AbortPolicy());

  /**
   * 提交带返回值的线程给线程池来运行
   *
   * @param task
   */
  public Future<?> submit(Callable task) {
    return pool.submit(task);
  }

  /**
   * 提交任务给线程池运行
   *
   * @param task
   */
  public Future<?> submit(Runnable task) {
    return pool.submit(task);
  }

  /**
   * 当前否为线程池已经满载
   *
   * @return
   */
  public boolean isFull() {
    return pool.getPoolSize() == pool.getMaximumPoolSize();
  }

  public String outPoolInfo() {
    StringBuilder outData = new StringBuilder();
    outData.append("pool Core Size:").append(pool.getCorePoolSize()).append(",");
    outData.append("curr pool size:").append(pool.getPoolSize()).append(",");
    outData.append("max pool Size:").append(pool.getMaximumPoolSize()).append(",");
    outData.append("queue size:").append(pool.getQueue().size()).append(",");
    outData.append("task completed size:").append(pool.getCompletedTaskCount()).append(",");
    outData.append("active count size:").append(pool.getActiveCount()).append(",");
    outData.append("task count size:").append(pool.getTaskCount()).append(",");
    outData.append(Symbol.LINE);
    outData.append(Symbol.LINE);

    return outData.toString();
  }

  public void shutdown() {
    pool.shutdown();
  }
}

order的改造

@RestController
@RequestMapping("/order")
public class OrderServerFacade {

  private Logger logger = LoggerFactory.getLogger(OrderServerFacade.class);

  private Gson gson = new Gson();

  /** 获取连接处理对象 */
  private RestTemplate restTemplate = this.getRestTemplate();

  @RequestMapping(
      value = "/orderInfo",
      method = {RequestMethod.POST})
  public ApiResponse getUserInfo(@RequestBody OrderDTO order) {

    logger.info("getUserInfo start {}", order);

    // 用户信息
    Future userRsp = this.asynchronousUser(order.getUserId());
    // 商品信息
    Future goodsRsp = this.asynchronousGoods(order.getGoodId());

    OrderDTO orderRsp = null;
    try {
      orderRsp = this.builderRsp(order, userRsp, goodsRsp);
    } catch (ExecutionException e) {
      logger.error("get userRps : ExecutionException  ", e);
    } catch (InterruptedException e) {
      logger.error("get userRps : InterruptedException  ", e);
    }

    ThreadUtils.sleep(1);

    logger.info("getUserInfo start {} rsp {} orderRsp {}  ", order, orderRsp);

    // 构建结果的响应
    return ApiResponse.ok(orderRsp);
  }

  /**
   * 异步调用用户服务
   *
   * @param goods 商品信息
   * @return 商品的结果
   */
  public Future asynchronousGoods(String goods) {
    Future rspData =
        ScheduleTaskThreadPool.INSTANCE.submit(
            () -> {
              return this.getGoods(goods);
            });

    return rspData;
  }

  /**
   * 异步调用用户
   *
   * @param userId
   * @return
   */
  public Future asynchronousUser(String userId) {
    Future rspData =
        ScheduleTaskThreadPool.INSTANCE.submit(
            () -> {
              return this.getUserInfo(userId);
            });

    return rspData;
  }

  /**
   * 构造响应
   *
   * @param userInfo 用户信息
   * @return 当前响应的用户信息
   */
  private OrderDTO builderRsp(OrderDTO orderInfo, Future userInfo, Future goodsInfo)
      throws ExecutionException, InterruptedException {
    OrderDTO order = new OrderDTO();
    order.setUserId(orderInfo.getUserId());
    order.setUserInfo((ClientUserDTO) userInfo.get());
    order.setGoodId(orderInfo.getGoodId());
    order.setGoodsInfo((ClientGoodsDTO) goodsInfo.get());
    return order;
  }

  /**
   * 获取用户的信息
   *
   * @param userId 用户的id
   * @return 用户的信息
   */
  private ClientUserDTO getUserInfo(String userId) {

    logger.info("request get user info start {} ", userId);

    ClientUserDTO clientUser = new ClientUserDTO();
    clientUser.setUserId(userId);
    HttpHeaders headers = new HttpHeaders();
    // 将对象装入HttpEntity中
    HttpEntity<ClientUserDTO> request = new HttpEntity<>(clientUser, headers);
    ResponseEntity<String> result =
        restTemplate.postForEntity("http://localhost:9000/user/getUserInfo", request, String.class);
    if (HttpStatus.OK.value() == result.getStatusCodeValue()) {
      ApiResponse<ClientUserDTO> data =
          gson.fromJson(result.getBody(), new TypeToken<ApiResponse<ClientUserDTO>>() {}.getType());
      // 如果操作成功,则返回结果
      if (data.getResult()) {
        ClientUserDTO rsp = data.getData();

        logger.info("request get user info start {} rsp {} ", userId, rsp);

        return rsp;
      }
    }
    return null;
  }

  /**
   * 获取商品信息
   *
   * @param dataId 商品信息
   * @return 当前的用户的信息
   */
  private ClientGoodsDTO getGoods(String dataId) {

    logger.info("request goods start {} ", dataId);

    ClientGoodsDTO clientGoods = new ClientGoodsDTO();
    clientGoods.setDataId(dataId);
    HttpHeaders headers = new HttpHeaders();
    // 将对象装入HttpEntity中
    HttpEntity<ClientGoodsDTO> request = new HttpEntity<>(clientGoods, headers);
    ResponseEntity<String> result =
        restTemplate.postForEntity(
            "http://localhost:9001/goods/getGoodsInfo", request, String.class);
    if (HttpStatus.OK.value() == result.getStatusCodeValue()) {
      ApiResponse<ClientGoodsDTO> data =
          gson.fromJson(
              result.getBody(), new TypeToken<ApiResponse<ClientGoodsDTO>>() {}.getType());
      // 如果操作成功,则返回结果
      if (data.getResult()) {
        ClientGoodsDTO goodsRsp = data.getData();
        logger.info("request goods start {} , response {} ", dataId, goodsRsp);
        return goodsRsp;
      }
    }
    return null;
  }

  /**
   * 获取连接管理对象
   *
   * @return
   */
  private RestTemplate getRestTemplate() {
    return new RestTemplate(getClientHttpRequestFactory());
  }

  /**
   * 获取连接处理的工厂信息
   *
   * @return
   */
  private SimpleClientHttpRequestFactory getClientHttpRequestFactory() {
    SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
    clientHttpRequestFactory.setConnectTimeout(25000);
    clientHttpRequestFactory.setReadTimeout(25000);
    return clientHttpRequestFactory;
  }
}

相比于同步阻塞的调用,用户查询与商品的查询都提交至了线程池中去执行发送请求的任务,并等待结果的响应,线程池中任务的执行结果使用Future来获取,这是一个等待的操作。

再来个启动函数

@SpringBootApplication
public class OrderApplication {

  public static void main(String[] args) {
    String[] argsNew = new String[] {"-- server.port=9010"};
    SpringApplication.run(OrderApplication.class, argsNew);
  }
}

我们做一个新的服务来测试下。

2.2 测试

单元测试的代码与同步阻塞相同.

现在来看下执行结果。

在这里插入图片描述

从结果中可以看到,新的调用都已经成功了,总耗时相比于同步调用,减少了5秒。仅耗时11秒。

2.3 分析

先看日志做一个基本的了解:

2021-03-12 07:52:45.281  INFO 1276 --- [nio-9010-exec-6] c.l.a.n.i.threadpool.OrderServerFacade   : getUserInfo start OrderDTO{userId='1001', userInfo=null, goodId='2001', goodsInfo=null}
2021-03-12 07:52:45.281  INFO 1276 --- [        order-0] c.l.a.n.i.threadpool.OrderServerFacade   : request get user info start 1001 
2021-03-12 07:52:45.281  INFO 1276 --- [        order-1] c.l.a.n.i.threadpool.OrderServerFacade   : request goods start 2001 
2021-03-12 07:52:50.287  INFO 1276 --- [        order-0] c.l.a.n.i.threadpool.OrderServerFacade   : request get user info start 1001 rsp UserDTO{userId='1001', name='bug_null', address='this is shanghai'} 
2021-03-12 07:52:55.287  INFO 1276 --- [        order-1] c.l.a.n.i.threadpool.OrderServerFacade   : request goods start 2001 , response Goods{dataId='2001', goodsPrice=1024, message='这是一个苹果,apple,还被咬了一口'} 
2021-03-12 07:52:56.288  INFO 1276 --- [nio-9010-exec-6] c.l.a.n.i.threadpool.OrderServerFacade   : getUserInfo start OrderDTO{userId='1001', userInfo=null, goodId='2001', goodsInfo=null} rsp OrderDTO{userId='1001', userInfo=UserDTO{userId='1001', name='bug_null', address='this is shanghai'}, goodId='2001', goodsInfo=Goods{dataId='2001', goodsPrice=1024, message='这是一个苹果,apple,还被咬了一口'}} orderRsp {}  

可以发现任务被分配到两个线程去执行。分别为order-0和order-1,然后等待结果响应。待所有响应都结束了,返回整个的调用结果。

同样的借助于jstack工具,查看线程栈的信息。

"order-1" #44 daemon prio=5 os_prio=0 tid=0x00000000210cc800 nid=0x534 runnable [0x0000000000d2e000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
	- locked <0x000000076c5da8f8> (a java.io.BufferedInputStream)
	at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:735)
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:678)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1593)
	- locked <0x000000076c5c97f0> (a sun.net.www.protocol.http.HttpURLConnection)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
	- locked <0x000000076c5c97f0> (a sun.net.www.protocol.http.HttpURLConnection)
	at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
	at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:55)
	at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:55)
	at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:766)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670)
	at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:445)
	at com.liujun.asynchronous.nonblocking.invoke.threadpool.OrderServerFacade.getGoods(OrderServerFacade.java:168)
	at com.liujun.asynchronous.nonblocking.invoke.threadpool.OrderServerFacade.lambda$asynchronousGoods$0(OrderServerFacade.java:82)
	at com.liujun.asynchronous.nonblocking.invoke.threadpool.OrderServerFacade$$Lambda$436/1379641878.call(Unknown Source)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- <0x00000006c560ee68> (a java.util.concurrent.ThreadPoolExecutor$Worker)
        
        
"order-0" #43 daemon prio=5 os_prio=0 tid=0x00000000210cf000 nid=0x1954 runnable [0x0000000000c2e000]
   java.lang.Thread.State: RUNNABLE
	at java.net.SocketInputStream.socketRead0(Native Method)
	at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
	at java.net.SocketInputStream.read(SocketInputStream.java:171)
	at java.net.SocketInputStream.read(SocketInputStream.java:141)
	at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
	at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
	- locked <0x000000076c9eaec8> (a java.io.BufferedInputStream)
	at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:735)
	at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:678)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1593)
	- locked <0x000000076c9da368> (a sun.net.www.protocol.http.HttpURLConnection)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1498)
	- locked <0x000000076c9da368> (a sun.net.www.protocol.http.HttpURLConnection)
	at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
	at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:55)
	at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:55)
	at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:766)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:670)
	at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:445)
	at com.liujun.asynchronous.nonblocking.invoke.threadpool.OrderServerFacade.getUserInfo(OrderServerFacade.java:136)
	at com.liujun.asynchronous.nonblocking.invoke.threadpool.OrderServerFacade.lambda$asynchronousUser$1(OrderServerFacade.java:98)
	at com.liujun.asynchronous.nonblocking.invoke.threadpool.OrderServerFacade$$Lambda$435/1688648333.call(Unknown Source)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- <0x00000006c560ec38> (a java.util.concurrent.ThreadPoolExecutor$Worker)
        


"http-nio-9010-exec-6" #29 daemon prio=5 os_prio=0 tid=0x00000000210bc000 nid=0x2e58 waiting on condition [0x0000000022f7c000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076cb6ba28> (a java.util.concurrent.FutureTask)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429)
	at java.util.concurrent.FutureTask.get(FutureTask.java:191)
	at com.liujun.asynchronous.nonblocking.invoke.threadpool.OrderServerFacade.builderRsp(OrderServerFacade.java:114)
	at com.liujun.asynchronous.nonblocking.invoke.threadpool.OrderServerFacade.getUserInfo(OrderServerFacade.java:57)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	- locked <0x00000006c544acb8> (a org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
	- <0x00000006c53e9668> (a java.util.concurrent.ThreadPoolExecutor$Worker)        

从栈的信息中可以看中。主线程会阻塞在获取结果响应中,也就是 - parking to wait for <0x000000076cb6ba28> (a java.util.concurrent.FutureTask)这个对象中,是线程的调用结果对象。而提交到线程池的两个任务,则会阻塞在:at java.net.SocketInputStream.socketRead0(Native Method)对象中,与同步阻塞的调用示例相同。

通过分析,我们就可以得到异步阻塞的一个示例图。

在这里插入图片描述

2.4 总结

优点:

此调用模型,将原本串行的任务,拆分到一个或者多个线程中去运行。这样任务就不用串行等待。提高了请求的响应时间,示例代码就从原本的16秒,减少到了11秒,省掉了一个请求的时间。任务中需要执行的操作越多。那么所提高的响应时间也是越好。

缺点:

那也很明显,首先是调用中的线程还是处于IO blocking中,这也导致了资源还是被浪费在了等待上;其次一个机器所能打开的线程有是限的。不能无限制的开线程,线程过多,将导致频繁的上下文切换。造成更大的资源浪费;再就是一个并行的场景存在限制,任务必须能够并行执行。不存在先后的关系,如果业务上存在在着先后顺序,就不能采用。

详细代码请至我的github:

https://github.com/kkzfl22/demojava8/blob/master/src/main/java/com/liujun/asynchronous/nonblocking/invoke/threadpool/OrderServerFacade.java

Java实现异步的原因、场景、实现异步的方式 有时某一个耗时很长的异步任务执行结束之后,你想利用它返回的结果再做进一步的运算,该运算也会是一个异步任务,两者之间的关系需要程序开发人员手动进行绑定赋予,Future并不能将其形成一个任务流(pipeline),每一个Future都是彼此之间都是孤立的,所以才有了后面的CompletableFuture,CompletableFuture就可以将多个Future串联起来形成任务流。响应式编程,其实就是对数据流的编程,而对流的处理对数据流的变化进行响应,是通过异步监听的方式来处理的。传送消息的链路称为信道。 阅读详情

相关推荐

网络-java中AIO(异步非阻塞)理解与应用

AIO(Asynchronous IO/异步非阻塞IO)概述: 是NIO的2.0版本,AIO是jdk1.7之后提供的,需要借助于本地操作系统(unix/liunx提供的异步IO模型)。 AIO最大的一个特性就是异步能力,这种能力对socket与文件IO都起作用。AIO其实是一种在读写操作结束之前允许进行其它操作的IO处理,AIO是对jdk1.4中提出的同步非阻塞NIO的进一步增强。 AIO对应IO...

Fly_Fly_Zhang的博客 2086

Java-主流框架—(5)Spring--异步调用

1.异步调用 1.1发送异步请求 <a href="javascript:void(0);" id="testAjax">访问controller</a> <script type="text/javascript" src="/js/jquery-3.3.1.min.js"></script> <script type="text/javascript"> $(function(){ $("#testAjax").click

qq_33513145的博客 620

JAVA中的异步调用

1.什么是异步调用 异步调用简单理解,就是不阻塞主线程,再开辟另一个线程异步执行 main{ //前置语句 costTime(); //后置语句 } 在上述代码中,主线程按顺序,需要依次执行前置语句,调用costTime函数,该函数耗时较长,以及后置语句。在执行costtime函数时,主线程处于阻塞状态。当后置语句和costTime函数执行无直接关联时,他们可以一起执行,这样的执行方式,就是对costTime的异步调用,此时主线程处于非阻塞状态。 2.简单实现异步调用 最简单的方式,就是new Th

Bazingaea的博客 2754

Java异步非阻塞编程的几种方式

文章目录方式一:同步调用方式二:Future方式异步调用方式三:Callback回调方式方式四:Java9 Reactive Streams 今天读到一篇博客,发现对Java异步非阻塞编程概念有些模糊了,趁此整理了一下常用的几种编程方式。 方式一:同步调用 当主线程发起IO操作时,会被阻塞,一直等到数据返回,此时主线程状态是Runnable状态。产生的问题是:主线程在IO等待的过程中,线程资源没有得到充分的利用,对于大量IO场景的业务吞吐量会有一定限制。如下图: 方式二:Future方式异步调用 使用fu

聚焦AI技术前沿,深度解析大模型原理与多场景应用,探索最新突破与落地实践,助力行业洞悉趋势,驱动智能转型,赋能千行百业。 2408

IATF16949核心职位全解析

TM / Top MgmtMR / QMRProcess OwnerQMQAQCSQECQEPQEPETEMEIEPM / PJMFMEA ModeratorMSA CoordinatorSPC CoordinatorCM / CCB内审员CP / CPK

weixin_49938552的博客 972

设备管理器找不到蓝牙?从驱动到BIOS的系统排查指南

在Windows系统中,蓝牙功能依赖硬件、驱动、系统服务三层协同工作。设备管理器作为硬件状态的可视化窗口,当蓝牙设备消失时,往往意味着某一层链路中断。常见原因包括BIOS无线开关关闭、驱动异常、蓝牙支持服务被禁用,或系统文件损坏。排查思路应从基础概念出发,先理解蓝牙设备在设备管理器中的四种隐藏位置,再通过扫描硬件、检查BIOS、重装驱动、修复服务等步骤逐层定位。这一方法不仅适用于Windows电脑,也适用于HC-05、ESP32等嵌入式蓝牙模块,因为硬件、协议栈、应用层的故障逻辑相通。掌握这套技术价值在于减

weixin_33377602的博客 267

Java 异步编程 (5 种异步实现方式详解)

首先我们先来看看一个同步的用户注册例子,流程如下:在同步操作中,我们执行到插入数据库的时候,我们必须等待这个方法彻底执行完才能执行“发送短信”这个操作,如果插入数据库这个动作执行时间较长,发送短信需要等待,这就是典型的同步场景。于是聪明的人们开始思考,如果两者关联性不强,能不能将一些非核心业务从主流程中剥离出来,于是有了异步编程雏形,改进后的流程如下:这就是异步编程,它是程序并发运行的一种手段,它允许多个事件同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行。

wy971744839的博客 3238

从仿真到打样:PMOS栅极泄放电路设计全流程复盘与三个踩坑点总结

本文详细复盘了PMOS栅极泄放电路从仿真到打样的全流程设计,重点总结了三个关键踩坑点:三极管BE结过压击穿、静态功耗超标和栅极振荡现象。通过优化缓启动电路、改进保护设计和布局调整,最终实现18ms开通时间、190μs关断时间和8μA静态功耗的工业级性能指标,为类似电源开关设计提供实用参考。

weixin_28693683的博客 370

Java实现异步的几种方式

Java实现异步的几种方式

weixin_45565886的博客 5329

Cadence Sigrity PowerSI实战:从.brd到S参数提取的完整避坑指南

本文详细介绍了使用Cadence Sigrity PowerSI进行信号完整性分析的完整流程,从.brd文件预处理到S参数提取的关键技巧。通过实战案例和避坑指南,帮助工程师高效解决高速PCB设计中的信号质量问题,提升仿真精度和设计效率。

carrot的博客 694

如何用Python实现SPEI指数计算?从数据准备到结果可视化完整指南

本文提供了一份完整的Python实现SPEI指数计算指南,涵盖从数据准备、潜在蒸散量计算到结果可视化的全流程。详细介绍了使用Thornthwaite方法计算PET、拟合三参数log-logistic分布以及标准化转换的核心算法,并提供了模块化代码和可视化方案,帮助用户构建可复用的干旱分析工具。

cola5的博客 267

Java 异步实现的几种方式

整理参考链接1 整理参考链接2 1. jdk1.8之前的Future jdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象,可以通过future获得执行的结果,但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询。 通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前...

Amber揣手手 11万+

FPGA,关于安装使用libero IDE V9.2 及其gold floating license配置

3、license安装根据,下面的提示进行,跟随setting up the license server on a host教程操作。1、从官网下载Libero IDE v9.2(如下图中的Libero IDE v9.2 software)4、下载完成license解压包后还需要下载 Daemons license ,地址如下。(2)第一行并不是在内填写,而是直接用您电脑名替换掉(1)修改license.bat前四行是,其务必保持和案例位置相同(避免出错)............

JasonK的博客 4294

java 怎么写异步方法_Java 异步实现的几种方式

1. jdk1.8之前的Futurejdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象,可以通过future获得执行的结果,但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询。通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前是不支持的,但是我们可以通过第三方的...

weixin_42350305的博客 8260

Trae 在代码审查场景的 3 大差异化能力:对比 Claude Code、Cursor 与 Codex 实测

揭示代码审查的核心矛盾是上下文断层而非找 Bug,介绍 Trae 通过跨文件符号追踪、意图-实现对齐校验、增量变更影响图谱生成三件事实现上下文校准,并对比其与 Claude Code、Cursor、Codex 在多模块 Java 项目中的实际审查差异

weixin_42525482的博客 151

喷涂机器人系列编程:ABB IRB 5500_(9).常见故障诊断与排除

ABB IRB 5500 喷涂机器人在实际应用中可能会遇到各种故障,但通过系统的诊断和排除方法,可以确保生产线的连续运行。定期维护和保养是预防故障的重要手段,而操作培训和规范则能有效减少人为因素引起的故障。希望本节内容能帮助操作和维护人员提高故障诊断和排除的效率,确保喷涂机器人的稳定运行。如果您有任何疑问或需要进一步的帮助,请联系 ABB 客户服务中心。

zhubeibei168的博客 1044

异步 java_Java 异步实现的几种方式

1. jdk1.8之前的Futurejdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象,可以通过future获得执行的结果,但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询。通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前是不支持的,但是我们可以通过第三方的...

weixin_34614674的博客 1348

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

java实现异步非阻塞的几种方式-消息中间件

java实现异步非阻塞的几种方式-消息中间件 4. 消息中间件 在前面已经实现了异步调用、接口回调两种形式,都较为复杂,需要自己控制多线程,那有没有简单点的方式来实现异步的消息通讯。这个当然是有的,可以使用消息中间件的形式,来实现整个消息的异步调用。还是以订单这个场景为示例来举个粟子。 4.1 样例代码 我的样例程序使用rabbitmq来做为消息队列传输数据,其他消息队列功能类似。 先从订单服务开始。 @RestController @RequestMapping("/order") public cla

bug_null的博客 786

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

OpenFeign - 支持异步调用:返回 CompletableFuture 实现非阻塞调用

本文介绍了如何在OpenFeign中使用CompletableFuture实现异步调用,提升微服务通信效率。主要内容包括: OpenFeign简介:声明式HTTP客户端,简化REST API调用 异步调用的必要性:解决同步调用的线程阻塞问题,提高系统吞吐量 技术实现方案:通过返回CompletableFuture实现非阻塞调用 具体实现步骤: 添加必要依赖 定义返回CompletableFuture的Feign接口 配置异步支持 使用示例 文章通过代码示例展示了异步调用的完整实现过程,并分析了异步模式相比同

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

从Java future 到 Guava ListenableFuture实现异步调用

本文是在学习中的总结,欢迎转载但请注明出处: 前言      随着移动互联网的蓬勃发展,手机App层出不穷,其业务也随之变得错综复杂。针对于开发人员来说,可能之前的一个业务只需要调取一次第三方接口以获取数据,而如今随着需求的增加,该业务需调取多个不同的第三方接口。通常,我们处理方法是让代码同步顺序的去调取这些接口。显然,调取接口数量的增加必然会造成响应时间的增加,势必会对系统性能造成一定影响。

皮斯特劳沃 2万+

java中怎么实现异步_Java 异步实现的几种方式

整理参考链接1整理参考链接21. jdk1.8之前的Futurejdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象,可以通过future获得执行的结果,但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询。通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前是不支持...

weixin_39732609的博客 2167

SpringBoot 使用 CompletableFuture 实现非阻塞异步编程

在前面的博客:https://blog.youkuaiyun.com/BiandanLoveyou/article/details/83586356 我们讲解了如何使用线程池Executor + Future +@Async 实现异步调用。今天讲解一个 JDK1.8 的异步类:CompletableFuture。 什么是非阻塞异步编程? 所谓非阻塞异步编程方法,简单地说,就是不用等待返回结果的多线程的回调方法的封装。相比于阻塞式异步回调方法,非阻塞异步编程方法使用一个监听器,这样在使用回调的过...

流放深圳 1万+

【Java 】异步实现的几种方式

Amber揣手手 2019-01-03 15:31:40 68057 收藏 83 整理参考链接1整理参考链接2 1. jdk1.8之前的Future jdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象,可以通过future获得执行的结果,但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询。 通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前是不支...

ooooooooooooooxiaosu的博客 285

回调函数、同步、异步、阻塞、非阻塞、D-BUS等概念的理解

同步、异步与阻塞、非阻塞 这个几个概念很容易被混淆。这些概念基本都是使用在多个部件进行协作或需要在一定时间内完成的场景中。 以前用过一个叫DSI的机制,用于分布式系统间的数据传递,能运行与Q-net和Socket之上,是一个master-slave的网络结构。不过这是一个私有的库,资料较少。 对应的开源的机制是D-BUS,只不过dbus是用于桌面应用程序之间的进程通信,或进程与系统内

viewcode的专栏 9006

java http异步调用_HttpClient的异步调用,你造吗?

一、前言HttpClient提供了两种I/O模型:经典的java阻塞I/O模型和基于Java NIO的异步非阻塞事件驱动I/O模型。Java中的阻塞I/O是一种高效、便捷的I/O模型,非常适合并发连接数量相对适中的高性能应用程序。只要并发连接的数量在1000个以下并且连接大多忙于传输数据,阻塞I/O模型就可以提供最佳的数据吞吐量性能。然而,对于连接大部分时间保持空闲的应用程序,上下文切换的开销可能...

weixin_35599225的博客 2846

Spring Cloud OpenFeign异步调用:非阻塞通信实现方案

Spring Cloud OpenFeign作为Spring Cloud生态中简化HTTP客户端开发的核心组件,提供了声明式API调用能力。在高并发场景下,传统同步调用会导致线程阻塞,影响系统吞吐量。本文将详解如何利用OpenFeign实现**非阻塞异步通信**,帮助开发者构建高性能微服务架构。 ## 异步调用的核心价值 在分布式系统中,服务间通信延迟是性能瓶颈的主要来源。传统同步调用模式下,

gitblog_00645的博客 773
上一篇: java实现异步非阻塞的几种方式-同步阻塞调用
下一篇: java实现异步非阻塞的几种方式-接口回调
null_null_bug
博客等级 码龄7年 72粉丝 71原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值