java多线程项目问题实践-01(重入锁)
- 程序运行时栈空了?
- 进入调试
- 分析为什么出现问题
- 重入锁解决问题
程序运行时栈空了
在跟踪bug的时候,发现stack的前一秒有size,下一秒就被清空了。在对stack进行清空的操作只有在循环语句之后才有,而当前还在循环。
public class MainClass{
public void a(final B b){
Stack stack=b.getSatck();
for(..){
....
stack.pop();
....
stack.push();
}
stack.clear();
}
}
public Class B{
Stack stack;
public Stack getSatck(){
return stack;
}
}
其中B类是一个有关上下文的类,保存的栈是一个全局共享的变量。
发现在for循环中对栈取数据时报了空指针错误。
进入调试
在a方法打断点,发现进来了两个线程,同时在跑,下面为模拟图(省略具体的代码):
分析问题
分析上面的代码,结合调试堆栈:
此时如果有多个线程同时调用a方法,虽然每个MainClass对象执行不同的a方法,但是在B作为参数传进来的时候已经被申明位final,此时多个线程就共用了b,如果一个线程已经执行到清空栈的语句,而另一个线程刚好在做pop和push操作,就会出现栈空的情况,在对栈拿数据时就会报空指针错误了。
重入锁
这种问题就是属于多个线程同时对一个对象操作了,需要考虑加锁。但是加在哪里,怎么加才好一点?经过调试,发现在a方法直接加synchronized同步语句是不可行的,因为多个线程执行不是同个Mainclass对象,对a方法加锁在本例中无效。最后在b方法中申明一个全局静态的ReentrantLock锁,即可解决问题。改善后的代码如下:
public class MainClass{
public void a(final B b){
B.lock.lock();//获得全局锁
try{
Stack stack=b.getSatck();
for(..){
....
stack.pop();
....
stack.push();
}
stack.clear();
}
fianlly{
B.lock.unlock();//释放全局锁
}
}
}
public Class B{
public static ReentrantLock lock = new ReentrantLock();//申明全局锁
Stack stack;
public Stack getSatck(){
return stack;
}
}
总结
在代码中平时要考虑多种情况的多线程并发争用问题,不同的问题要具体分析,学会调试也是一种基本功。鉴于目前对多线程还需要加深理解,如有更好方法,欢迎补充。