最近在springboot中需要用到定时器,永不停歇那种,timer,其实就是新增加一条线程,然后我还想往线程里传入变量,然后我还想在线程里改变变量的值。这个是很危险的动作,琢磨了一下这个java的多线程,做一下记录。
一、错误示范
1、当你在函数中定义一个变量,想要在后面生成的定时器中传入值。如果你像这样传入:
@RequestMapping(value = "/test")
@ResponseBody
public void test(HttpServletRequest request,HttpSession session){
String test="";
Timer timer=new Timer();
timer.schedule(new TimerTask() {
public void run() {
test="another value";
}
}
这是一定会报这样的错的:
Local variable test defined in an enclosing scope must be final or effectively final
原因很简单,就是告诉你只有传入线程的值只能是final属性。
2、然后,你就开始改啊改:
@RequestMapping(value = "/test")
@ResponseBody
public void test(HttpServletRequest request,HttpSession session){
final String test="";
Timer timer=new Timer();
timer.schedule(new TimerTask() {
public void run() {
test="another value";
}
}
然后,又会出现新的错误:
The final local variable test cannot be assigned, since it is defined in an enclosing type
这句话的意思就是告诉你,因为你定义的值是final,所以是不能被赋值的。现在你心里是不是一万只草泥马奔腾而过。
所以,常规手段是没用的。
二、正解
1、把变量定义在全局上。
String test="";
@RequestMapping(value = "/test")
@ResponseBody
public void test(HttpServletRequest request,HttpSession session){
Timer timer=new Timer();
timer.schedule(new TimerTask() {
public void run() {
test="another value";
}
}
这个方法可以解决一部分问题,但是不能解决所有问题,就是当多个前端都调用这个test()函数映射时,这就混乱了,变量一会被这个前端改成了这个值,一会又被另一个前端改成了那个值。这个方法只能用在修改一个全局参数的情况下,这个参数是所有前端都需要修改的那种。
2、定义数组传入。
有人问为什么数组传入了线程还能改变,这不是bug吗?不是的,数组其实也有不能改变的属性,这个属性我看到有一个网友解释说是,数组的长度。数组的长度是不能改变的。
不过这就方便了我们在函数中定义一个数组传入线程。
@RequestMapping(value = "/test")
@ResponseBody
public void test(HttpServletRequest request,HttpSession session){
String[] test=new String[1];
Timer timer=new Timer();
timer.schedule(new TimerTask() {
public void run() {
test[0]="another value";
}
}
三、其他需要注意的点
1、不要被那些报错以及网上的只言片语给误导了,往另一个线程传值不需要把变量的属性定义为final,那些报错只是想告诉你,在线程中有些属性不能改变,而不是说要你给变量属性加上final,传值到线程内,跟不是线程内没有任何区别,不一样的,只是赋值,在线程内给线程外的变量赋值需要费一番波折。
2、如果你是从前台传过来的值,又刚好跟我一样习惯用request.getParameter(“xxx”)来取值,那你一定要注意,不要把取值这句话写到了线程里面去,你可以写在线程外面,或者直接用注释取值:
错误示范:
@RequestMapping(value = "/test")
@ResponseBody
public void test(HttpServletRequest request,HttpSession session){
String[] test=new String[1];
Timer timer=new Timer();
timer.schedule(new TimerTask() {
public void run() {
String mvalue=request.getParameter("value");//在这
test[0]="another value";
}
}
正确示范:
@RequestMapping(value = "/test")
@ResponseBody
public void test(HttpServletRequest request,HttpSession session){
String mvalue=request.getParameter("value");//在这
String[] test=new String[1];
Timer timer=new Timer();
timer.schedule(new TimerTask() {
public void run() {
test[0]="another value";
}
}