实践springmvc如何使用异步-以及为何使用异步,@Async,@EnableAsync,DeferredResult,Callable

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/y41992910/article/details/89963168

异步需要线程池的支持,需要先配置一个线程池
在spring 的配置文件中写入

	<task:executor id="executorPortal" pool-size="4-16" queue-capacity="1000000" />
	<task:annotation-driven executor="executorPortal" />

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;
/**
 * 测试进行异步
 * @author he_guanhong
 *
 */
@RequestMapping("/test")
@Controller
public class TestController {

	/**
	 * http://127.0.0.1:8080/portal/test/test
	 * 测试springmvc可以有几个主线程来处理
	 * 会不会有满的情况,经过测试,主线程达到五个就会满了,这个跟系统的硬件配置,软件配置有关,反正就是有限的.
	 * 然后其他后续来的请求,就开始阻塞和等待.
	 * 
	 *  线程:54,的开始时间2019-05-08 04:39:52
		线程:57,的开始时间2019-05-08 04:40:15
		线程:58,的开始时间2019-05-08 04:40:17
		线程:59,的开始时间2019-05-08 04:40:19
		线程:60,的开始时间2019-05-08 04:40:21
		线程:61,的开始时间2019-05-08 04:40:24
		
		线程:54,的结束时间2019-05-08 04:41:32
		线程:54,的开始时间2019-05-08 04:41:32
		线程:57,的结束时间2019-05-08 04:41:55
		线程:57,的开始时间2019-05-08 04:41:55
		线程:58,的结束时间2019-05-08 04:41:57
		线程:55,的开始时间2019-05-08 04:41:57
		线程:59,的结束时间2019-05-08 04:41:59
		线程:56,的开始时间2019-05-08 04:41:59
		线程:60,的结束时间2019-05-08 04:42:01
		线程:61,的结束时间2019-05-08 04:42:04
		线程:54,的结束时间2019-05-08 04:43:12
		线程:57,的结束时间2019-05-08 04:43:35
		线程:55,的结束时间2019-05-08 04:43:37
		线程:56,的结束时间2019-05-08 04:43:39
	 * @return
	 */
	@RequestMapping("/test")
	@ResponseBody
	public String test(){
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		long id = Thread.currentThread().getId();
		System.out.println("线程:" +id +  ",的开始时间" + simpleDateFormat.format(new Date()));
		//模拟进行一项非常耗时的工作
		try {
			Thread.currentThread().sleep(1000*100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("线程:" +id +  ",的结束时间" + simpleDateFormat.format(new Date()));
		return "测试";
	}
	/**
	 * 
	 * 
	 * http://127.0.0.1:8080/portal/test/testyibu
	 * 
	 * 可以看到,使用异步之后,主线程可以接受更多的请求,
	 * 
	 * 主线程:54,的开始时间2019-05-08 05:05:06
主线程:54,的结束时间2019-05-08 05:05:06
副线程开始...Thread[MvcAsync1,5,main]==>2019-05-08 05:05:06
主线程:58,的开始时间2019-05-08 05:05:17
主线程:58,的结束时间2019-05-08 05:05:17
副线程开始...Thread[MvcAsync2,5,main]==>2019-05-08 05:05:17
主线程:61,的开始时间2019-05-08 05:05:28
主线程:61,的结束时间2019-05-08 05:05:28
副线程开始...Thread[MvcAsync3,5,main]==>2019-05-08 05:05:28
主线程:64,的开始时间2019-05-08 05:05:28
主线程:64,的结束时间2019-05-08 05:05:28
副线程开始...Thread[MvcAsync4,5,main]==>2019-05-08 05:05:28
主线程:49,的开始时间2019-05-08 05:05:30
主线程:49,的结束时间2019-05-08 05:05:30
副线程开始...Thread[MvcAsync5,5,main]==>2019-05-08 05:05:30
主线程:53,的开始时间2019-05-08 05:05:31
主线程:53,的结束时间2019-05-08 05:05:31
副线程开始...Thread[MvcAsync6,5,main]==>2019-05-08 05:05:31
主线程:54,的开始时间2019-05-08 05:05:32
主线程:54,的结束时间2019-05-08 05:05:32
副线程开始...Thread[MvcAsync7,5,main]==>2019-05-08 05:05:32
主线程:57,的开始时间2019-05-08 05:05:33
主线程:57,的结束时间2019-05-08 05:05:33
副线程开始...Thread[MvcAsync8,5,main]==>2019-05-08 05:05:33
主线程:58,的开始时间2019-05-08 05:05:38
主线程:58,的结束时间2019-05-08 05:05:38
副线程开始...Thread[MvcAsync9,5,main]==>2019-05-08 05:05:38
主线程:61,的开始时间2019-05-08 05:05:39
主线程:61,的结束时间2019-05-08 05:05:39
副线程开始...Thread[MvcAsync10,5,main]==>2019-05-08 05:05:39
副线程结束...Thread[MvcAsync1,5,main]==>2019-05-08 05:05:56
副线程结束...Thread[MvcAsync2,5,main]==>2019-05-08 05:06:07
副线程结束...Thread[MvcAsync3,5,main]==>2019-05-08 05:06:18
副线程结束...Thread[MvcAsync4,5,main]==>2019-05-08 05:06:18
副线程结束...Thread[MvcAsync5,5,main]==>2019-05-08 05:06:20
副线程结束...Thread[MvcAsync6,5,main]==>2019-05-08 05:06:21
副线程结束...Thread[MvcAsync7,5,main]==>2019-05-08 05:06:22
副线程结束...Thread[MvcAsync8,5,main]==>2019-05-08 05:06:23
副线程结束...Thread[MvcAsync9,5,main]==>2019-05-08 05:06:28
副线程结束...Thread[MvcAsync10,5,main]==>2019-05-08 05:06:29

Callable,在完成call()方法之后返回信息
	 * @return
	 */
	@RequestMapping("/testyibu")
	@ResponseBody
	public Callable<String> test2(){
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		long id = Thread.currentThread().getId();
		System.out.println("主线程:" +id +  ",的开始时间" + simpleDateFormat.format(new Date()));
		//模拟进行一项非常耗时的工作
		Callable<String> callable = new Callable<String>() {
			@Override
			public String call() throws Exception {
				SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
				System.out.println("副线程开始..."+Thread.currentThread()+"==>"+simpleDateFormat.format(new Date()));
				//估计是等待时间过长,50s,所以就返回了503的错误,证明确实如此,
				//connectionTimeout:网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。
				//tomcat默认的等等时间为20s
				//<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
				//Thread.sleep(50000);
				/**
				 * 2. 如何加大tomcat连接数
						在tomcat配置文件server.xml中的<Connector />配置中,和连接数相关的参数有:
						minProcessors:最小空闲连接线程数,用于提高系统处理性能,默认值为10
						maxProcessors:最大连接线程数,即:并发处理的最大请求数,默认值为75
						acceptCount:允许的最大连接数,应大于等于maxProcessors,默认值为100
						enableLookups:是否反查域名,取值为:true或false。为了提高处理能力,应设置为false
						connectionTimeout:网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。
						其中和最大连接数相关的参数为maxProcessors和acceptCount。如果要加大并发连接数,应同时加大这两个参数。
						web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。tomcat5中的配置示例:
						    <Connector port="8080"
						              maxThreads="150" minSpareThreads="25"maxSpareThreads="75"
						              enableLookups="false" redirectPort="8443"acceptCount="100"
						              debug="0" connectionTimeout="20000" 
						              disableUploadTimeout="true" />
						对于其他端口的侦听配置,以此类推。
				 */
				Thread.sleep(5000);
				System.out.println("副线程结束..."+Thread.currentThread()+"==>"+simpleDateFormat.format(new Date()));
				return "Callable<String> async01()";
			}
		};
		
		Callable<String> callable2 = new Callable<String>() {

			@Override
			public String call() throws Exception {
				// TODO Auto-generated method stub
				return null;
			}
		};
		System.out.println("主线程:" +id +  ",的结束时间" + simpleDateFormat.format(new Date()));
		return callable;
	}
	
	private static DeferredResult<String> deferredResult = null;
	@RequestMapping("/testyibu2")
	@ResponseBody
	public DeferredResult<String> test3(){
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		long id = Thread.currentThread().getId();
		System.out.println("主线程:" +id +  ",的开始时间" + simpleDateFormat.format(new Date()));
		//模拟进行一项非常耗时的工作
		deferredResult = new DeferredResult<String>((long) 50000, "超时了");
		System.out.println("主线程:" +id +  ",的结束时间" + simpleDateFormat.format(new Date()));
		return deferredResult;
	}
	
	
	@RequestMapping("/testyibu3")
	@ResponseBody
	public String test4(){
		deferredResult.setResult("返回结果给他");
		return "test4";
	}
	
}


在controller层使用异步接收请求.

在service层也用异步作为请求.

1.在controller上使用@EnableAsync,修饰controller,使得controller可以支持异步

2.在对应的service方法上使用@Async,使得方法异步.

输出结果

主线程:54,的开始时间2019-05-09 10:25:00
主线程:54,的结束时间2019-05-09 10:25:00
 testAsync 当前线程id:55, 当前线程名称:executorPortal-1

代码


import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;
/**
 * 测试进行异步
 * @author he_guanhong
 *
 */
@RequestMapping("/test")
@Controller
@EnableAsync
public class TestController {

	@RequestMapping("/testyibu2")
	@ResponseBody
	public DeferredResult<String> test3(){
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		long id = Thread.currentThread().getId();
		System.out.println("主线程:" +id +  ",的开始时间" + simpleDateFormat.format(new Date()));
		//模拟进行一项非常耗时的工作
		
		DeferredResult<String> deferredResult = new DeferredResult<String>((long) 50000, "超时了");
		//这样不起作用
		//testAsync(deferredResult);
		asyncServiceTest.testAsync(deferredResult);
		System.out.println("主线程:" +id +  ",的结束时间" + simpleDateFormat.format(new Date()));
		return deferredResult;
	}
	@Autowired
	AsyncServiceTest asyncServiceTest;
}

AsyncServiceTest

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.async.DeferredResult;

@Service
public class AsyncServiceTest {

	@Async
	public void testAsync(DeferredResult<String> deferredResult){
		
		System.out.println(" testAsync 当前线程id:" + Thread.currentThread().getId() + ", 当前线程名称:" + Thread.currentThread().getName());
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		deferredResult.setResult(" testAsync 当前线程id:" + Thread.currentThread().getId() + ", 当前线程名称:" + Thread.currentThread().getName());
	}
}

猜你喜欢

转载自blog.csdn.net/y41992910/article/details/89963168