java多线程之一小步

针对多线程的编程,首先要注意的就是线程安全问题,但是我们应该采取哪些措施来避免这些问题呢,看过java并发实践之后,真是有些恍然的感觉。

1.首先要查看你所使用的对象所在的上下文,会不会多线程访问;

  我们写的大部分程序都是过程性的,这也是为什么很多人在学java开发时,可以根本不考虑其面向对象的特质。同样的,我们每天写的代码也多数是在单线程下运行的,即便你或者他在写web代码。因为真正在多线程处理的是java容器,是你所使用的框架,真正到我们的写代码的那一层,跟多线程相关的已经很少很少。但实际coding时,还是有需要我们注意的地方。比如,你的确需要写一个servlet,这个时候就面临了多线程的处理情况。或者你需要编写一个后台的服务提供程序,这时你也会碰到多线程的情形。总之,别偷懒,好好分析一下你的代码环境是极有必要的。

2.如果是多线程访问,要检查此对象是不是可变对象(mutable),变化越少,也就越容易变成一个线程安全的对象;

编写java代码时,最基本的就是写一个类,然后实施最有效的代理方式,也就组合方式。通过借用已经存在的对象动作,来组成一个新的类。此时我们要检查我们的类所依赖的对象是不是经常的发生变化。请参照下一条。

3.尽量将所有的field设计成final的方式,除非实在必要;

一般情况下,mutable对象中的每个依赖的引用如果经常发生变化,我们的对象就是不稳定的对象。比方说,我们每个人都是一个完整的有机整体,如果有胳膊有腿的话。但是如果是胳膊腿整天变,就肯定是个残疾人或者是机器人了,你觉得这是个稳定状态吗?你敢确定其每次用手扶着你的时候,总是能使一样劲吗?对于拥有这样的状态的对象,我们要注意。最好是能保证其所依赖的各个引用是一成不变的。当然,仅仅是引用不变也是不能保证线程安全的。如果我们所依赖的对象是一个集合类,它的状态也会发生改变,在多线程环境中,我们就需要了解以下的一些信息了。

4.不变的对象本身就是线程安全的;

5.封装使得管理复杂问题更加实际;

    我们对外过于张扬,明显会招致别人的误解。对于多线程环境下,我们把状态设置成全局变量也会使得我们处于多难的境地。但是封装在对象中,我们却可以对其访问进行管理。

6.给每个变化的变量加一把锁:

  上面的这么多状态问题,在这里都可以解决。我们的field是变化,不管是引用还是状态,如果它是容易发生改变的,我们就应该给任何操作它的方法加上一把锁。使得使用和操作它的线程都拿的舒心,用得称心。但是这里有个很严肃的问题,那就是如何选择这把锁。而这个问题也正是我们第三条中提到的,我们要看这些变化的数据是处于哪个范围的。以我们自己的写的对象MyClass为参照。

  1)如果是MyClass中的一个引用(一个field),经常发生变化,这把锁就应该是MyClass的本身锁(java中的每个类都有一个锁)。那么所有针对这个field的操作方法,都要加synchronized关键字。或者有些同僚仅仅是对某些操作这个field的方法中的某个区域加同步,此时也应该是synchronized(this).

  2)如果我们的field不是mutable,but this field is a collection,and then the data inside will change because of multi-thread.We will have use the lock on this field.注意,一定要注意,千万要注意,这里需要synchronized(theFieldReference)!!!

从以上这两个方面看,确定我们要访问的数据范围,是我们确定锁的依据。在此顺便提一下:有些源码中,在使用synchronized关键字时,不是使用this,也不是使用具体的field,而是用了如此的代码Object lock = new Object();各位看官,这里也一定要注意,别大眼漏神了,前面还应该有一个final,即final Object lock = new Object();为什么需要这样来做呢?两个原因:first,使用this,也就是类的intrinsic lock,容易被人家(应该不是啥好心人,当然也有可能是无心人)通过denial-of-service[Effective java,2001]来攻击,什么意思呢,就是通过获取你对象的固有锁,进而阻止(block)其他进程对你对象的访问;second,请参考第一个。呵呵,完整的使用方式:private final Object lock = new Object(); 这里使用final的目的是为了在创建了这个对象之后,引用就只有一个,当然,所有的synchronized(lock)也会很明确地施加在这个lock对象上。倘若没有用final,如果这lock的引用被改掉(原因多了,即便是private,除非你对天发誓,这个引用不变,不会再new一个),那这个锁就不确定到底是哪一个啦。看看spring的源码中到处都充斥着private final Object lock = new Object();现在明白了吗?

7.给不变量或者不变区域中的所有对象施加同一把锁;

  接上第六条,如果一个代码块(比方说是一个方法,或者方法中的一部分)中,有一个以上的变量会被修改,尤其是这个代码块是多线程调用的,这个时候,一定要查看锁的范围,不要仅仅使用一个变量的锁。

8.要加锁的地方,别开着!

posted on 2011-05-01 16:33  eric_chen  阅读(411)  评论(0编辑  收藏  举报