Из личного примера. Я делал космические захватчики для личного роста, и он использовал несколько Thread
с. Один Thread
обработал рендеринг и другую обработанную логику игры. Это до того, как я убедился в параллельности и как правильно реализовать его на Java.
Anyways, у меня был ArrayList<Laser>
, который держал все Laser
s в системе на любом данном фрейме в игре (или так я думал). Из-за природы космических захватчиков List
был чрезвычайно динамичным.Он постоянно добавлялся в качестве новых Laser
s, порожденных и удаленных, поскольку они либо ушли с карты, либо столкнулись с сущностью.
Это работало хорошо и хорошо, за исключением случаев, когда я так часто получал ConcurrentModificationException
. Мне потребовалось много времени, чтобы точно выяснить, что происходит. Оказывается, что рендеринг Thread
был в редких случаях пойманным итерированием через List<Laser>
одновременно с тем, что игровая логика Thread
либо добавляла новые Laser
s, либо удаляла их.
Это связано с тем, что когда Thread
1 получает указатель на место пятна в памяти, оно почти похоже на то, что оно «работает» на этом блоке памяти. Thread
2 приходит и хватает, что тот же указатель не знает, что объект уже находится на «операционном столе», измененном Thread
1, и он пытается сделать то, что он намеревался, только чтобы найти то, что Thread
2 думал, что это действительно известно об объекте не удалось из-за изменений Thread
. Это то, что заканчивается броском ConcurrentModificationException
.
Это можно решить несколькими различными способами. Я думаю, что самый эффективный и безопасный способ решить это сейчас - это API-интерфейс Java 8 Stream
(если все сделано правильно, оно обеспечит true параллелизм), или вы можете использовать блоки (я думаю, что они появились на Java 5). С блоками synchronized
текущий Thread
, который смотрит на объект, по существу заблокирует его, не позволяя другим Thread
s даже наблюдать за объектом. Как только Thread
будет выполнен, он освободит объект для следующего Thread
для его работы.
Вот два примера того, как использовать synchronized
:
public synchronized void xamp1(List<Laser> lasers){
for(Laser l:lasers){
//code to observe or modify
}
}
public void xamp2(List<Laser> lasers){
synchronized(lasers){
for(Laser l:lasers){
//code to observe or modify
}
}
}
Объекты не копируются автоматически. Вы должны следить за тем, чтобы несколько потоков не изменяли один и тот же объект одновременно. См. Это [учебник о параллелизме] (например, http://docs.oracle.com/javase/tutorial/essential/concurrency/). – Jesper