并发编程 — 实现线程安全


并发编程系列文章:
      初解线程池:http://ray-yui.iteye.com/blog/2072463
      详解线程池:http://ray-yui.iteye.com/blog/2075311
      并发数据类型:http://ray-yui.iteye.com/blog/2080454
      并发数据结构:http://ray-yui.iteye.com/blog/2084202
      volatile:http://ray-yui.iteye.com/blog/2231474
      判断线程安全:http://ray-yui.iteye.com/blog/2232931
      实现线程安全:http://ray-yui.iteye.com/blog/2234688


      在多线程程序开发当中,可以使用一些编程技巧规避线程安全问题或减少使用锁导致的额外开销.


1.线程封闭
      当多个线程访问修改同一对象时需要使用同步机制,其中一种规避方法是不共享任何数据.参数传入线程时传入变量的复制.或所有变量/对象都从线程当中创建.这种方法将所有线程所能触及的变量都封闭在线程内部本身,即使被封闭的对象不是线程安全,也可以达到线程安全的效果.


2.实例封闭
      当一个对象被封闭在另一个新创建的对象当中时,能访问该对象的所有代码路径都是已知的.例如struct2中的Action就是使用实例封闭的方式实现线程安全.框架保证每一个请求都创建一个全新的Action对象,从而将线程安全委托给实例封闭


3.栈封闭
      栈封闭的使用方式在三层架构当中非常常用.通常使用在封闭的方法当中,方法只操作传入参数进行业务处理.例如service只操作通过上层传入的参数调用DAO层,SpringMVC由于Controller是单例对象,所有的操作都在方法级别进行栈封闭从而实现线程安全.


4.ThreadLocal
      ThreadLocal在多线程的Web系统中应用非常广泛,如Struct2的ActionContext.JSF的FacesContext.Spring的Transactional都是通过ThreadLocal实现.ThreadLocal允许被置入的对象访问域提升到线程的级别.但使用ThreadLocal时需要注意,这是一种变量侵入式的设计.ThreadLocal的设置位置可能和读取位置相隔甚远,甚至无法从代码调用的上下文中寻找到设置该变量的位置.这导致业务逻辑依赖ThreadLocal.


5.不变对象
      不变对象天生对多线程环境友好.当一个对象创建后就没有任何代码路径会修改对象中的属性.由于没有提供任何方法对其属性修改,因此可以自由地在多线程中共享访问.但需要注意并非不提供setter方法就视为不变对象,而是所有暴露出去的方法中没有任何一个会对属性进行修改.对象的所有属性在创建对象时已经确定.并且不会有改变


6.委托并发容器发布对象
      Java当中提供了很多线程安全的容器.当一个对象通过线程安全容器进行发布时,我们可以认为这个对象已被安全的发布出去并处于线程安全状态.


总结:
      以上实现线程安全的方式都不曾提及公有的静态变量.而公有的静态变量往往是容易被忽略导致线程安全问题发生的原因.包括一些全局的作用域例如HttpServletRequest操作Application作用域时,由于Application是全局共享的,所以当操作Application时仍然需要同步操作.

猜你喜欢

转载自ray-yui.iteye.com/blog/2234688
今日推荐