Dostęp do static ArrayList z 2-ch różnych wątków

0

Piszę program umożliwiający grę w szachy dwóm osobom.
W klasie Jajca (mniejsza o nazwę) mam statyczne pole typu ArrayList przechowujące wszystkie figury, które są na szachownicy.

Kiedy następuje promocja piona, zostaje wywołane nowe okienko (po SwingUtilities.invokeLater()), gdzie po kliknięciu przycisku tworzy nową figurę (obiekt promotedPawn), przyjmijmy, hetmana i zamienia promowany pionek (siedzący na pozycji index)na tą figurę:

				Jajca.getPieces().set(index, promotedPawn);

Gdy odpowiedzią na promocję jest zabicie świeżo wypromowanej figury, czyli tego hetmana...

public void doMove()
	{
		captured.removeFromBoard();  //zdejmuje zbijaną figurę a szachownicy
		super.doMove();  //ustawia figurę bijącą na odpowiednie pole
		indexOfCaptured=Jajca.getPieces().indexOf(captured);
		Jajca.getPieces().set(indexOfCaptured, null);  //usuwa zbijaną figurę z tablicy
	}

...oczekuję, że pod referencją captured jest hetman i indexOfCaptured będzie równy indeksowi pionka, który został usunięty (jak mi się wydaje) z tablicy. Tymczasem wartość indexOfCaptured wynosi -1 i wywala błąd w następnej linijce. Jak sprawdziłem, podczas wykonywania linijki
indexOfCaptured=Jajca.getPieces().indexOf(captured);
pod referencją captured kryje się... pionek.
Jak to naprawić?
Wydaje mi się, że problem może leżeć w tym, że ArrayList nie jest, jak to się mówi, thread-safe i/lub braku synchronizacji.

0

A to tworzysz sam jakieś wątki? Jeśli tak to jaka jest kolejność ich wykonywania i co one robią? Problemy ze spójnością danych są tylko gdy dwa wątki operują na zmiennej w tym samym czasie (tzn co najmniej jeden wątek zapisuje).

Do synchronizacji list jest Collections.synchronizedList(list).

Używasz invokeLater poza tworzeniem okna?

0

Dziwna ta implementacja. Po pierwsze jak wątki budują obraz szachownicy. Jeżeli budowany jest on jednorazowo to w zmiennej captured masz trzymany pion przed promocją zatem w momencie promocji następuje utrata spójności danych. Wątek ma referencję do piona, lista ma referencjhę do hetmana.
Po drugie jak zabezpieczona jest lista. Czy korzystasz z synchronizacji?
Po trzecie czy promotedPawn jest nowym obiektem czy tylko zmieniasz flagę w klasie?

0

Kod budujący okienko promocji:

public class PromotionDialog extends JDialog
{
	
	private ArrayList<JButton> buttons;
	private final String[] names={"queen", "rook", "bishop", "knight"};
	private Color color;
	private String wb;

	public PromotionDialog(MainWindow w, final Pawn p) throws IOException
	{
		
		super(w,"Na jaką figurę promować piona?",true);
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		setResizable(false);
		setLayout(new FlowLayout(FlowLayout.CENTER,20,20));
		
		class PromoteIntoListener implements ActionListener
		{
			@Override
			public void actionPerformed(ActionEvent e)
			{
				int index=Jajca.getPieces().indexOf(p);
				
				int file=p.getPosition().getFile();
				int rank=p.getPosition().getRank()+1;
				int i=buttons.indexOf(e.getSource());
				
				
				p.removeFromBoard();
				Chessman promotedPawn=null;
				switch(i)
				{
				case 0: promotedPawn=new Queen(color, Jajca.getChessboard(), file, rank); break;
				case 1: promotedPawn=new Rook(color, Jajca.getChessboard(), file, rank); break;
				case 2: promotedPawn=new Bishop(color, Jajca.getChessboard(), file, rank); break;
				case 3: promotedPawn=new Knight(color, Jajca.getChessboard(), file, rank);
				}

				Jajca.getPieces().set(index, promotedPawn);

				promotedPawn.generateMoveList();
				
				MainWindow.diagram.remove(MainWindow.diagram.piecesLabels.get(index));
				//...
				dispose();
			} 
		 }
		PromoteIntoListener pil=new PromoteIntoListener();
		
		buttons=new ArrayList<JButton>();
		color=p.getColor();

		if(color==Color.WHITE)
			wb="W";
		else
			wb="B";

		for(int i=0;i<4;++i)
		{
			buttons.add(i,new JButton(ImageLoader.loadAndScaleImage("img/"+wb+names[i]+".png", 75, 75)));
			buttons.get(i).setPreferredSize(new Dimension(75,75));
			buttons.get(i).addActionListener(pil);
			add(buttons.get(i));
		}
		pack();
		setVisible(true);
	} 
}

Przy promocji dokonuje się zamiana elementu tablicy. Potem, kiedy wykonuję bicie, do zmiennej captured jest pobierana figura do zbicia na podstawie pola, na którym się znajduje. A tam, jak pokazuje kod, powinien stać hetman.

Koziołek napisał(a)

Po pierwsze jak wątki budują obraz szachownicy.

Szachownica to osobny obiekt budowany jednorazowo - tablica dwuwymiarowa 8x8 zapełniona obiektami reprezentującymi poszczególne pola.

Koziołek napisał(a)

Po drugie jak zabezpieczona jest lista. Czy korzystasz z synchronizacji?

Przyznam się, że nie.

0

Ha ;) Jeżeli figura jest pobierana z pola szachownicy to pytanie czy pole przetrzymuje prawidłowy obiekt, czy też ma referencje do starej figury. Analizując problem tzn. zmiana i od razu bicie można założyć, że pole szachownicy posiada referencję do starej figury, a powinno posiadać do nowej.

0

Pole szachownicy ma referencję na nową figurę. Odpowiednią modyfikację wykonuje jej konstruktor.

0

@niko1as, sprawdź debuggerem, bo moim zdaniem taka zmiana nie następuje.

0

Po kłopocie!

Co się okazało:

  • pion wchodzi na linię przemiany. W związku z tym zostają odświeżone ruchy figur, które mogły przesunąć się na pole zajęte przez tego piona
  • czyli pojawia się możliwość jego bicia, przy czym pionek zostaje zapamiętany w zmiennej captured (co było złym pomysłem)
  • następuje promocja. Zamiast pionka pojawia się hetman, zarówno w tablicy, jak i na szachownicy
  • no i przy próbie bicia pobierana jest zmienna captured, gdzie jest referencja na nieistniejący już w tablicy pionek i nic dziwnego, że indexOf() zwracał -1.

Wielkie dzięki za poświęcony czas.

1 użytkowników online, w tym zalogowanych: 0, gości: 1