线程安全与共享资源
介绍
允许被多个线程同时执行的代码称作线程安全的代码。线程安全的代码不包含竞态条件。
弄清楚Java线程执行时共享了哪些资源非常的重要
共享资源
局部变量
局部变量存储在线程自己的栈中.也就是说,局部变量永远也不会被多个线程共享。所以,基础类型的局部变量是安全的.
public int fun(){
int a=10;
return a;
}
这个方法就是线程安全的
局部的对象引用
对象的局部引用和基础类型的局部变量不太一样.尽管引用本身没有被共享.但引用所指的对象并没有存储在线程的栈内.
所有的对象都存在共享堆中.
如果在某个方法中创建的对象不会逃逸出(即该对象不会被其它方法获得,也不会被非局部变量引用到)该方法,那么它就是线程安全的.
public void fun(){
Object obj=new Object();
//obj do ...
}
//这个方法即使是局部对象引用也是线程安全的,因为每一个线程都会创建一个新的Object对象。
对象成员(即成员变量)
对象成员存储在堆上.如果两个线程同时更新同一个对象的同一个成员,那这个代码就不是线程安全的.
以下这段方法代码就不是线程安全的,需要我们作同步处理。
private int count = 0;
public int incr()
{
return (++count);
}
线程控制逃逸规则
如果一个资源的创建、使用、销毁都在同一个线程内完成,且永远不会脱离该线程的控制,则该资源的使用就是线程安全的.
资源可以是对象、数组、文件、数据库连接、套接字等等.Java中你无需主动销毁对象,所以“销毁”指不再有引用指向对象。
即使对象本身线程安全,但如果该对象中包含其他资源(文件,数据库连接)整个应用也许就不再是线程安全的了
比如2个线程都创建了各自的数据库连接,每个连接自身是线程安全的,但它们所连接到的同一个数据库也许不是线程安全的
2个线程执行如下代码
检查记录X是否存在,不存在则插入X
如果两个线程并发执行可能的情景:
A线程检查记录X是否存在。结果不存在
B线程检查记录X是否存在。结果不存在
A线程插入记录
B线程插入记录
这样就会出现重复插入问题了。
同样的问题也会发生在文件或其他共享资源上.因此,区分某个线程控制的对象是资源本身,还是仅仅到某个资源的引用很重要。
总结
线程安全问题首先要分析共享资源是什么,有时除了程序上的共享资源,还得考虑文件、数据库等资源。
参考
1、http://tutorials.jenkov.com/java-concurrency/thread-safety.html