这礼拜被人问到2次关于action是否是线程安全的?论坛也时常看到有关于线程安全是什么的提问。线程安全到底是什么。现在谈安全,那就是说本来的是不安全的。所以觉得应该转换下问题变成什么是线程不安全。
自己写了个例子,很多线程同时访问一个对象。
测试结果如下
instanceVar是Thread-0 localVar是Thread-0等待...
instanceVar是Thread-1 localVar是Thread-1等待...
等待结束 instanceVar是Thread-1 localVar是Thread-0
等待结束 instanceVar是Thread-1 localVar是Thread-1
instanceVar是Thread-1 localVar是Thread-1等待...
等待结束 instanceVar是Thread-1 localVar是Thread-0
等待结束 instanceVar是Thread-1 localVar是Thread-1
从结果的第3行可以看到局部变量是Thread-0,最后类成员却变成Thread-1
得到一个结论:
对象方法中局部变量是在单个线程的工作内存中操作,不共享。
对象成员存在于公共内存中,和所有线程共享。
所以对象成员有可能不会出现本来预想中的值,也就称之为“
线程不安全”。
那么具体发生错误的经过是怎么样的呢,为什么要加一句Thread.sleep(1)。
如果
注释Thread.sleep(1),那把循环数加到到50。会得到下面的结果
测试结果如下:(太长,只区的局部片断)
instanceVar是Thread-0 localVar是Thread-0等待...
等待结束 instanceVar是Thread-0 localVar是Thread-0
instanceVar是Thread-1 localVar是Thread-1等待...
等待结束 instanceVar是Thread-1 localVar是Thread-1
instanceVar是Thread-2 localVar是Thread-2等待...
等待结束 instanceVar是Thread-2 localVar是Thread-2
instanceVar是Thread-3 localVar是Thread-3等待...
等待结束 instanceVar是Thread-3 localVar是Thread-3
instanceVar是Thread-4 localVar是Thread-4等待...
等待结束 instanceVar是Thread-4 localVar是Thread-4
instanceVar是Thread-5 localVar是Thread-5等待...
instanceVar是Thread-6 localVar是Thread-6等待...
等待结束 instanceVar是Thread-0 localVar是Thread-0
instanceVar是Thread-1 localVar是Thread-1等待...
等待结束 instanceVar是Thread-1 localVar是Thread-1
instanceVar是Thread-2 localVar是Thread-2等待...
等待结束 instanceVar是Thread-2 localVar是Thread-2
instanceVar是Thread-3 localVar是Thread-3等待...
等待结束 instanceVar是Thread-3 localVar是Thread-3
instanceVar是Thread-4 localVar是Thread-4等待...
等待结束 instanceVar是Thread-4 localVar是Thread-4
instanceVar是Thread-5 localVar是Thread-5等待...
instanceVar是Thread-6 localVar是Thread-6等待...
.......(省略)
instanceVar是Thread-47 localVar是Thread-47等待...
instanceVar是Thread-48 localVar是Thread-48等待...
等待结束 instanceVar是Thread-48 localVar是Thread-5
等待结束 instanceVar是Thread-48 localVar是Thread-6
等待结束 instanceVar是Thread-48 localVar是Thread-7
instanceVar是Thread-47 localVar是Thread-47等待...
instanceVar是Thread-48 localVar是Thread-48等待...
等待结束 instanceVar是Thread-48 localVar是Thread-5
等待结束 instanceVar是Thread-48 localVar是Thread-6
等待结束 instanceVar是Thread-48 localVar是Thread-7
可以看到一开始比较正常,但是一到后来有的线程执行到一半就没有执行,等到线程48执行到一半,原来没有执行完的线程又开始执行。但是对象变量和局部变量却出错了。
这说明
有的线程执行UnSafeObject的process()并没有执行到完毕,时间片就让给了别人,等再次得到时间片的时候,原先的实例变量已经被改变了,所以出现错误。而Thread.sleep
是制造了这个过程。
ps:
当前测试环境jdk1.5_9 eclipse,同样的代码在jdk1.5 editplus中会有不同,循环次数加大就可以看到该现象。
写到这里,我突然明白其实这个问题非常简单,就是初学java时介绍同步方法是说的那个问题。但是同步会降低效率。
其实所谓servlet,action的线程安全问题也是这个问题。效率和安全的取舍!
servlet,action如何解决线程安全和效率问题?不采用同步且避免使用类成员