1.线程安全概述
使用多线程可以在一段时间内并发处理多个任务,在提高CPU运行效率的同时也为我们批量处理这些任务带来了便利。但是,使用多线程的时候要格外小心,多个线程在某一时间对同一个变量的处理,如果处理不当,就会造成数据不一致的问题,出现的这种数据不一致的现象就是非线程安全。非线程安全是多线程才会出现的问题。
上面的情况只是非线程安全的一种。非线程安全出现的原因是各个线程的控制流彼此独立,线程的执行需要线程调度程序管理,他们之间的执行顺序是随机的不确定的,而各个线程共享资源,所以多线程会带来线程调度,同步,死锁等一系列的问题。产生非线程安全问题的原因在于对共享数据访问操作的不完整性。
非线程安全针对的是共享数据的情况。共享数据如全局变量,这些变量对多个线程来说可以同时访问,同时访问的过程中就会有出现问题的可能。私有数据如方法内部的实例变量,这种变量的作用域只在方法本身,出了方法体就无法访问。对于私有数据,多线程同时访问就不存在线程安全的问题,所以线程执行的结果总是线程安全的,这是方法内部的变量是私有的特性造成的。
2.和线程安全有关的实际例子
(1)一个工资管理人员正在修改雇员的工资表,而一些雇员同时正在领取工资,如果允许这样做,必然会引起工资发放的混乱。
(2)实现投票功能时,多个线程可以同时处理同一个人的票数。
(3)两个线程A和B在同时使用Stack的同一个实例对象。A正在往堆栈里push一个数据,B则要从堆栈中pop一个数据。
class Stack{
int idx=0;
char[ ] data = new char[6];
public void push(char c){
data[idx] = c;
idx++;
}
public char pop(){
idx--;
return data[idx];
}
}
(1)操作之前,栈中的数据如图所示,此时idx = 2。
q |
p |
(2)A执行push中的第一个语句,将r推入堆栈,idx = 2;
r |
q |
p |
(3)A还未执行idx++语句,A的执行被B中断,B执行pop( )方法,返回q,idx = 1,此时堆栈中数据同(2);
A继续执行push的第二条语句,idx = 2;最后的结果相当于q没有出栈,r也没有入栈。此时堆栈中数据同(2)。<