OO多线程debug第一篇---多线程出现bug的原因

昨天oo第五次作业,在ddl前半小时发现了一个bug,我猜到可能与sunchronized同步互斥的范围没有调整好有关系,但是通过IDEA的debug模式和JProfile的视图都没有很快找出bug。最后不出所料的话,这次的作业又将是一片红。以后OO作业一定要周二晚或者周三早上就开始,在周五中午前要完成代码。剩余的一天半用来构建测评机和debug。

再说一说,我最后是用print大法找出了bug。其实我想到用print之后,很快就发现了其实原因如下。

private LinkedBlockingQueue<Person> pout;
    private  LinkedBlockingQueue<Instruction> instrs;
    private Elevator elevator;

	...
            synchronized (pout) {
                while (hasPerson()) {
                    try {
                        look();
                        pout.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                pout.notifyAll();
            }
   ...

本次我的设计中,只有input.run方法, elevator实例的executeInstuction方法和controller实例的look方法能改变pin, pout 和 instrs这三个LinkedBlockingQueue队列的值。

当hasPerson()为True时,进入while循环体,look方法有elevator的锁保证同步互斥。但是仍然有可能出错:进入循环体后,在进入look方法前,elevator进程的executeInstruction方法占用了elevator锁,然后将pout清空,之后再释放锁,当look方法得到elevator锁时,会发现pout队列已经是空的,因此执行可能出错。

我们再看看另一个多线程导致的错误。

例如执行以下方法add()时,可能实际虚拟机执行的是注释里的三行代码,如果这时同时用两个进程A\B都调用了同一个实例的add方法,那么如果执行顺序是A1, B1, A2, A3, B2, B3, 那么最后cnt的值只加了一而不是预料中的加二。

...
public add() {
	this.cnt =+ 1;
	// int a = this.cnt;
	// a = a + 1;
	// this.cnt = a;
}

我们再想想,多线程为什么容易出bug?原因是多个线程同时修改或者访问某一个变量(实例域),而结果与执行顺序相关,而执行顺序本就是不确定的,导致实际操作不符合我们预料中的逻辑。

反过来说两个线程结果确定性的充分条件是:两个线程的要写的变量没有交集;并且一个线程要写的变量和另一个线程要读的变量没有交集。

但还有导致结果错误的关键的一环没有说出来: 多个进程访问同一变量,导致整个代码块的逻辑不能顺畅被执行,因此导致错误。

因此,避免多线程错误的基本方法就是:将前后逻辑相关的代码块整个划分出来,并保证这个代码块的原子性。

我在这次作业中犯的错误就是判断hasPeron与look方法是一个完整的逻辑,我却将其分为了两部分,只给look方法上了锁。修改如下:

private LinkedBlockingQueue<Person> pout;
    private  LinkedBlockingQueue<Instruction> instrs;
    private Elevator elevator;

	...
            synchronized (pout) {
                while (True) {
	                synchronized (elevator) {
		                if (hasPerson)  {
			                    try {
			                        look();
			                        pout.wait();
			                    } catch (InterruptedException e) {
			                        e.printStackTrace();
			                    }
			             }
		             }
	             }
                pout.notifyAll();
            }
   ...

小结:多线程debug方法:

  • IDEA的debug功能
  • JProfile的识图
  • 出现前两者难以发现的bug时,可以尝试print大法

下一章将讲述具体怎么保证代码块的原子性.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值