关于JAVA并发编程的易错点总结
概述
并发编程,是一个十分广泛的概念,这里主要以Java的并发编程编程为例进行一些介绍。这里的并发编程,指的是多个线程,而非多个进程。前者可以看作是一台电脑上多个共享资源的机器同时运行,后者可以看作多台电脑同时运行。而并发编程易错点和难点也恰恰在资源共享这个问题上,本篇文章主要以HIT软件构造课程Lab6为例介绍下Java并发编程常见的一些易错点。
常见错误及实例
-
死锁错误
这是一个老生常谈的话题了,如果在这个地方只是重复前人的结论,那么接下来的工作是毫无意义的,所以我在此处提出一些新的看法。从我的角度来看,死锁错误分为直接和间接两种。前面一种的成因容易理解和发现,类似于下面这个情景:A,B两个人同时吃着自己碗里,还想抢对方碗里的饭就纠缠到一起,引起更多的人来看热闹;这样一来,餐厅就乱成了一锅粥。后者的成因则较为隐蔽,在给出实例之前,我们依然从一个现实场景入手(尽管现实中没有这么zz的设计):有这样一条单行通道,它的通行方向由第一辆通行汽车的前进方向确定。然而总会有人钻规则的空子,他在发现对面的车即将进入车道时,同时也进入了车道。两辆车你不让我,我不让你,道路就堵死了。当然,后面这种错误是由于其隐蔽性和偶然性是很难被发现的,只能尽量把这种错误扼杀在摇篮中。在编程时,把准备进入车道和进入车道合并成一个原子动作。通俗来讲,就是一步到位,不让其他人有空子可钻。我们来看下面这个例子:
public static Ladder getLadderMethod1(Monkey monkey) { for (Ladder l : ladders) { if (l.getDirection().equals("")) { l.setDirection(monkey.getDirection()); l.setBegin(monkey); return l; } return null; }
假设Monkey A调用这个方法并选择了Ladder L,但是还没有上到梯子上;此时切换到了Monkey B,他也发现这个L是空的并选择了L,并且两个Monkey相向过河。这样一来,两人就要在河中间相遇了。。。
-
对局部变量加锁
用一句来形容这种错误就是:你禁锢的只是我的影子,而不是我本人。这种情况Java静态编译会有警告提示,然而由于忽略警告的习惯使我对编辑器的警告免疫了?。用本次实验的实例来说明,在猴子每一步过河时都要对梯子进行加锁,以防猴子A在半空中的时候被猴子B抢占了位置(是的,坏猴子总是B来做(摊手))。但由于开始时是对在Monkey类中局部变量Ladder L进行加锁,所以有的时候位置被B给抢了。
-
不能锁住类中的属性
再坚固的所也只能锁住我的躯体,而不能锁住我的心。在对List ladders这个变量加锁时,原以为锁住了关于它的一切,实际上只是锁住了对ladders的引用。在进行梯子选择时,其他猴子依然可以在已经选择的梯子上进行各种操作,所以ladder的锁还是要继续增加。