提交 9c73ca56 编写于 作者: Q qiurunze

提交threadlocal讲解

上级 133946bd
<component name="libraryTable">
<library name="Maven: com.google.guava:guava:18.0">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/18.0/guava-18.0.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/18.0/guava-18.0-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/com/google/guava/guava/18.0/guava-18.0-sources.jar!/" />
</SOURCES>
</library>
</component>
\ No newline at end of file
此差异已折叠。
......@@ -92,7 +92,33 @@ redis的数量不是库存,他的作用仅仅只是为了阻挡多余的请求
1.具体我会有时间更新关于redis的知识
### <font color=#0099ff size=3 >15.rabbitmq如何做到消息不重复不丢失即使服务器重启?</font><br>
1.exchange持久化2.queue持久化3.发送消息设置MessageDeliveryMode.persisent这个也是默认的行为4.手动确认
1.exchange持久化2.queue持久化3.发送消息设置MessageDeliveryMode.persisent这个也是默认的行为4.手动确认
\ No newline at end of file
### <font color=#0099ff size=3 >15.为什么threadlocal存储user对象,原理??</font><br>
![整体流程](http://i2.bvimg.com/601558/3293e36cc2c7e303.png)
1.并发编程中重要的问题就是数据共享,当你在一个线程中改变任意属性时,所有的线程都会因此受到影响,同时会看到第一个线程修改后的值<br>
有时我们希望如此,比如:多个线程增大或减小同一个计数器变量<br>
但是,有时我们希望确保每个线程,只能工作在它自己的线程实例的拷贝上,同时不会影响其他线程的数据<br>
举例: 举个例子,想象你在开发一个电子商务应用,你需要为每一个控制器处理的顾客请求,生成一个唯一的事务ID,同时将其传到管理器或DAO的业务方法中,以便记录日志。一种方案是将事务ID作为一个参数,传到所有的业务方法中。但这并不是一个好的方案,它会使代码变得冗余。
你可以使用ThreadLocal类型的变量解决这个问题。首先在控制器或者任意一个预处理器拦截器中生成一个事务ID
然后在ThreadLocal中 设置事务ID,最后,不论这个控制器调用什么方法,都能从threadlocal中获取事务ID
而且这个应用的控制器可以同时处理多个请求,
同时在框架 层面,因为每一个请求都是在一个单独的线程中处理的,所以事务ID对于每一个线程都是唯一的,而且可以从所有线程的执行路径获取
运行结果可以看出每个线程都在维护自己的变量:
Starting Thread: 0 : Fri Sep 21 23:05:34 CST 2018<br>
Starting Thread: 2 : Fri Sep 21 23:05:34 CST 2018<br>
Starting Thread: 1 : Fri Jan 02 05:36:17 CST 1970<br>
Thread Finished: 1 : Fri Jan 02 05:36:17 CST 1970<br>
Thread Finished: 0 : Fri Sep 21 23:05:34 CST 2018<br>
Thread Finished: 2 : Fri Sep 21 23:05:34 CST 2018<br>
局部线程通常使用在这样的情况下,当你有一些对象并不满足线程安全,但是你想避免在使用synchronized关键字<br>
块时产生的同步访问,那么,让每个线程拥有它自己的对象实例<br>
注意:局部变量是同步或局部线程的一个好的替代,它总是能够保证线程安全。唯一可能限制你这样做的是你的应用设计约束<br>
所以设计threadlocal存储user不会对对象产生影响,每次进来一个请求都会产生自身的线程变量来存储
\ No newline at end of file
......@@ -49,6 +49,7 @@
<orderEntry type="library" name="Maven: org.springframework:spring-context:4.3.12.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:4.3.12.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:4.3.12.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:18.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-thymeleaf:1.5.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf-spring4:2.1.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.thymeleaf:thymeleaf:2.1.5.RELEASE" level="project" />
......
......@@ -24,7 +24,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
......
package com.geekq.miaosha;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class DemoTask implements Runnable
{
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>()
{
@Override
protected Integer initialValue()
{
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public int getThreadId()
{
return threadId.get();
}
// Returns the current thread's starting timestamp
private static final ThreadLocal<Date> startDate = new ThreadLocal<Date>()
{
protected Date initialValue()
{
if(threadId.get()==1){
return new Date(77777777L);
}
return new Date();
}
};
@Override
public void run()
{
System.out.printf("Starting Thread: %s : %s\n", getThreadId(), startDate.get());
try
{
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.printf("Thread Finished: %s : %s\n", getThreadId(), startDate.get());
}
}
\ No newline at end of file
package com.geekq.miaosha;
public class Test {
public static void main(String[] args) {
DemoTask demoTask = new DemoTask();
DemoTask demoTask1 = new DemoTask();
DemoTask demoTask2 = new DemoTask();
Thread t = new Thread(demoTask);
t.start();
new Thread(demoTask).start();
new Thread(demoTask2).start();
}
}
package com.geekq.miaosha.config;
import com.geekq.miaosha.access.UserContext;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.service.MiaoShaUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
......@@ -13,7 +13,6 @@ import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
......@@ -28,16 +27,18 @@ public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest webRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
String paramToken = request.getParameter(MiaoShaUserService.COOKIE_NAME_TOKEN);
String cookieToken = getCookieValue(request, MiaoShaUserService.COOKIE_NAME_TOKEN);
if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
return null;
}
String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
return userService.getByToken(response, token);
// HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
// HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
//
// String paramToken = request.getParameter(MiaoShaUserService.COOKIE_NAME_TOKEN);
// String cookieToken = getCookieValue(request, MiaoShaUserService.COOKIE_NAME_TOKEN);
// if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
// return null;
// }
// String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
// return userService.getByToken(response, token);
//threadlocal 存储线程副本 保证线程不冲突
return UserContext.getUser();
}
private String getCookieValue(HttpServletRequest request, String cookiName) {
......
......@@ -66,12 +66,22 @@ public class MiaoshaController implements InitializingBean {
return Result.error(CodeMsg.SESSION_ERROR);
}
//验证path
boolean check = miaoshaService.checkPath(user, goodsId, path);
if(!check){
return Result.error(CodeMsg.REQUEST_ILLEGAL);
}
// //使用RateLimiter 限流
// RateLimiter rateLimiter = RateLimiter.create(10);
// //判断能否在1秒内得到令牌,如果不能则立即返回false,不会阻塞程序
// if (!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
// System.out.println("短期无法获取令牌,真不幸,排队也瞎排");
// return Result.error(CodeMsg.MIAOSHA_FAIL);
//
// }
//是否已经秒杀到
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if(order!=null){
......
package com.geekq.miaosha.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/ratelimiter")
public class RateLimiterController {
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册