重新写了一次坦克大战,以前自己写一个新类用synchronized同步所有方法,现在发现java自己提供了要给,记录一下使用过程中遇到的问题。
Collections.synchronizedList
最开始我以为直接使用返回的值就可以了,后来发现完全没用,正确的用法是用synchronized关键字把对象先锁住然后再使用。
List list = Collections.synchronizedList(new ArrayList());
...
synchronized(list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
酱紫就好了。PS:咦,既然用synchronized加锁了,那这个方法到底干了啥?(研究中)
然后我终于实现了同步,可以把坦克,子弹,保证效果这些绘制的内容安全的添加和删除了。
BUT!新问题出现了,画面有时候会卡一下,频率比较看心情,画面已经实现了双缓冲,应该不是绘制的问题,可能是我把所有计算放在同一个线程中太复杂,计算速度赶不上绘制速度,所以我把不同部分的计算放到了两个线程中(现在看来好像并没有什么用,两个线程反而增加了线程调度的开销更慢了才对),情况继续存在。
突然天使飞过,我想到可能是同步了容器以后,所有的操作都在同步块里面执行,导致容器被阻塞了,所以我想着尽可能把代码放到同步块外面,但是由于需要频繁的操作容器中的对象,并且进行计算,最重要的是我用了迭代器,好像并没有什么好方法可以解决,如下。
synchronized (bullets) {
for(Bullet b:bullets){
//.....
// long code
翻了api以后发现容器有一个toArray方法,还有一个泛型的实现!美翻了,最后它变成了这样
synchronized (bullets) {
arr = bullets.toArray(new Bullet[0]);
}
for(Bullet b:arr){
//.......
//long code
这样进行计算的时候就不会锁住容器了,实验一下。。。BINGO!
这个故事告诉我们用synchronized锁住一个对象的时候不要在里面进行很变态的计算!,可取的行为是只有在直接对容器进行操作的时候锁住,使用结束以后立刻释放。如果容器会被频繁访问,但又需要依次对容器内的对象进行操作,转成数组,然后迭代数组,最后将结果更新到容器中,这样将容器短暂的锁住两次(或是多次)而不是一直锁住容器