闲谈线程安全问题

9/6/2015来源:Java教程人气:1152

闲谈线程安全问题

一、什么是线程安全问题?

  谈到线程安全,那么程序必然是运行在多线程的环境中才会有这样的问题。那是不是只要是多线程的应用的代码都有线程安全问题呢?答案显然是否定的,比如我们写的Action就是一个运行在多线程环境中的代码,web服务器接收到一个http请求就会创建一个Thread来处理请求,但是我们的Action为啥没有方法都没有加同步呢?原因是我们的action对象,每次接收到一个http请求(一个Thread),都会重新创建(new)一个新的action对象,就是不同线程使用的action对象是不同的。说完action,我们再说说service吧,如果项目使用了sPRing,我们都知道spring容器中的对象默认是single(单例),也就是程序中service对象只有一个,那么service对象被不同的线程调用的时候使用的都是一个service对象。所以一般情况下service对象应该是一个无状态的对象,也就是不包含属性,如果在某些情况下,你给一个service定义了一个属性,service方法中对该属性进行了读和写操作,这个时候还要不要考虑线程安全问题呢?答案是非常有必要了!

你定义的这个属性就是一个被多线程共享的资源,同时多线程有读写操作,就有可能(注意是有可能)存在线程安全问题。

  总结一下判断一个程序存不存在线程安全问题的方法:

  1、程序运行在多线程环境下吗?

  2、多线程是否会共享一个资源并且对这个共享资源有读和写操作?

  如果满足以上两点那就非常有必要考虑线程安全问题。

  所以上面的代码就是要考虑线程安全问题了。但是你可能会说为啥我知道可能会有线程安全问题,我却又没有做任何同步处理?原因是这样的,要考虑线程安全问题并不代表一定就有线程安全问题。仿佛有点矛盾。因为我这个业务决定了这里不存在线程安全问题,又或者即使这里发生了线程安全问题,导致的结果是可以预计并且不会对系统产生影响的。所以这里我并没有做同步控制。所以说是判断存不存在线程安全问题,还要根据业务特点和发生问题导致的结果来判断。

二、如何解决线程安全问题

1、将对象设置成无状态的

比如上面的service对象尽量不要设置属性,那么这个service对象就是无状态的,多线程使用这个service对象时不存在共享资源问题。

2、使用局部对象

如果上面的activeTimer是一个方法内的对象那么也不存在线程安全问题,因为多线程调用同一个对象的方式时,方法内的对象是私有的。

3、如果对象中不得不使用属性时,比如上面的activeTimer必须要设置成service对象属性,那么可以考虑使用ThreadLocal包装属性,包装后的activeTimer就是一个线程安全的,ThreadLocal实际上是为每个线程都重现创建一个activeTimer对象,各线程用自己的activeTimer,也就避免了线程安全问题,当然导致各线程修改的activeTimer不被共享,需要根据自己的业务特点使用。

4、当以上3种办法都不适合的时候,终极大招出来了,使用线程同步技术。把读写共享资源的代码块上把锁,给锁起来,让多线程调用这段代码的时候按顺序来访问,这样就不存在线程安全了。java中实现线程同步(互斥)的方法主要有两种,使用synchronized和lock。