U01_010 Servlet3存值之线程安全

1. 应用域线程安全问题

概念:

  • 应用域,也叫上下文作用域,也叫application域,它是线程不安全的。
  • 应用中的每一部分都能访问application域中存放的属性,这就意味着可能有多个servlet(意味可能有多个线程)同时操作application域,因为请求是并发处理的,每个请求在一个单独的线程中处理,不论这些请求指向同一个servlet还是不同的servlet,都是如此。
  • 假设一下
    • A线程进来 setAttribute("money", "100")
    • A线程挂起
    • B线程进来 setAttribute("money", "200")
    • B线程挂起
    • A线程苏醒
    • A线程调动 getAttribute("money"),得到的居然是 200
  • 解决方案:使用同步块锁住同步资源,锁类型使用上下文对象本身,虽然牺牲了一点效率,但这已经是相比之下的最佳方案了!
synchronized(getServletContext()){
    app.setAttribute("money", "100");
    System.out.println(app.getAttribute("money"));
}

注意,不能使用this锁,或者给 doGet() 或者 doPost() 方法上加锁,因为非静态方法上的锁也是this锁,这意味只能阻止同一个Servlet中的其他线程,但是并不能阻止不同的servlet的线程。

2. 会话域线程安全问题

概念:

  • HttpSession中存放的属性同样也是线程不安全的。
  • 虽然一个浏览器同一时间只能发出一个请求,但试想一下你有没有过这种情况,比如你打开一个窗口后响应太慢,你又不想一直等着,于是又开一个新窗口,或者你把之前的窗口最小化结果忘了,于是又开了一个新窗口…总之,即使是同一个浏览器,可以能发出多个请求,此时给HttpSession中的共享资源做线程保护,也是很有必要的。
synchronized(session){
    app.setAttribute("money", "100");
    System.out.println(app.getAttribute("money"));
}

注意,不能使用this锁,或者给 doGet() 或者 doPost() 方法上加锁,因为非静态方法上的锁也是this锁,这意味只能阻止同一个Servlet中的其他线程,但是并不能阻止不同的servlet的线程。

3. 请求域线程安全问题

概念:

  • request域中存放的资源是线程安全的,因为这些资源只在一次请求中,这次请求结束了,资源也就销毁了。
  • 所以我们在设计servlet的时候,尽量将数据存放到request域中,或者尽量都定义在 doGet()doPost() 方法体内的局部变量中,这样就可以避免百分之八十的线程安全问题。
  • 总结:请求属性和局部变量是servlet中线程最安全的位置!

4. SingleThreadModel设计模式

概念:

  • SingleThreadModel 接口,也称为STM接口,是为了保证一个servlet一次只处理一个请求而创建的(和同步doGet()/doPost()方法的效果是一样的),使用方法很简单,只要让你的servlet实现这个接口即可。
  • 它有两种策略,一种是让请求排队,一种是维护Servlet实例池,并发处理请求,具体使用哪种策略取决于容器开发商,但这两种策略,前者牺牲太多效率,后者违反了Servlet的单例规范,所以目前STM已经被ServletAPI废弃掉了。
  • 对于STM,它早已退出江湖,而你只需要知道,这名战士,它曾经试图保护过Servlet的属性的线程安全,就足够了。

https://juejin.im/post/5ea150136fb9a03c6568feb7

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论