何为线程安全?
我们对一个逻辑进行操作N次,无论串行操作还是并行操作,得到的结果都一样,那么我们称之为线程安全的逻辑。
此处可以类比幂等:
一个操作进行1次或者重复执行N次,如果得到的结果相同,此操作就是幂等的,反之则不是幂等操作。
为什么会线程不安全?
牵涉到jvm的内存分配模型。如以下代码:
public int addOne(int i){ return i+1; }
我们定义一个变量int a,然后多线程调用a,并用addOne方法对a进行增加操作100次。此时有一个关键问题:**变量a是线程专有还是线程共享?**显然线程专有不会有安全问题,线程共享则会造成结果不一致。
怎样判断相关变量是共享还是专有?
简单方法1:
根据变量的声明和使用是否在同一个线程内来判断。
int a = 0 ;
dealAWithMutilThread(a)
以上代码,变量a的声明在多线程操作之前,也就是说a的声明在线程操作之外,则a是线程共享的。
简单方法2:
根据变量的内存位置判断。
如果一个变量分配在堆(heap)上,则为共享变量。如果分配在线程中,则不一定(线程嵌套情况中,即使在栈上的变量也不安全)
怎样判断一个变量在堆中还是在栈中?
当我们new一个对象,jvm会对其进行逃逸分析(标量替换?),优先分配于线程的栈桢。栈桢内存仍位于堆但属于线程私有(JDK1.8)。更加详细的信息读者可自行搜索了解。
通常来讲,线程哪些数据是私有,哪些数据是共享?
程序计数器,局部变量值(寄存器),栈指针。统称线程上下文。
另:一个进程包含多个线程,单cpu同一时间只能进行一个计算,也就是单线程计算。当需要cpu计算其他线程的结果时(即线程栈桢中的代码块),就
要切换上下文,造成性能消耗。
怎样解决线程安全问题?
锁。
详见:多线程编程笔记2–线程安全的解药:锁