前言:做HuaweiDB比赛时,使用了一个CountDownLatch,开16个线程将key-off文件中的key与off写入map中。线程中定义了一个n用来计数,打印n时发现n出现了跳跃从117直接跳跃到238,我就在思考是不是并发时出现其他线程也修改了n的值,因为线程中n要自增。因此就想了解一下处理何种对象时,会出现竞态。
1.局部变量
局部变量是存储在线程自己的栈中的,局部变量不会被多个线程共享。
1.1基础类型的局部变量
线程安全
1.2局部对象引用
引用也被存在线程的栈中,不会被共享,但引用指向的对象,是存放在堆中的,如果这个对象不会被其他方法获得,也不会被非局部变量引用,高级一点的词叫做在该方法中创建的对象不会逃逸,此时这个对象是线程安全的。如果这个对象可能会被其他方法获得,或者是会被非局部变量引用,此时线程是不安全的。在其他线程执行的使用了该对象的方法里,该对象是会被改变的。
2.对象成员
对象成员存储在堆上。如果两个线程同时更新同一个对象的同一个成员,此时可能出现错误。因此对象成员也是线程不安全的。
3静态变量
静态变量是可以被所有线程都看到的,所有线程都能对他进行修改,因此也是线程不安全的。
举个栗子
一个单例类(懒汉 到需要时才创建对象)当有两个线程A和B都调用了getInstance这个方法时,此时A和B同时调用getInstance,A判断instance为null,在还没有创建对象之前,此时B也判断instance是否为null,因此也创建了一个LazyInitRace对象,并返回结果。这种情况A和B返回的不是同一个对象,如果该类被用于初始化注册表时,就会出现严重的错误,因为返回了不同的实例,有某些注册信息会丢失掉。
public class LazyInitRace{
private static LazyInitRace instance = null;
public static LazyInitRace getInstance(){
if(instance == null){
instance = new LazyInitRace();
}
return instance;
}
}
如何确保线程安全
1.创建不可变的共享对象。
2.加锁。