Konkurencyjność pomimo synchronizacji

0

Witam wszystkich.

Piszę program w którym obiekty (każdy jest osobnym wątkiem) poruszają się na mapie robiąc ruch w górę, prawo, dół lub lewo - o jedną pozycję. Swoją mapę zorganizowałem na dwuwymiarowej tablicy intów, którą opakowałem klasą Przestrzen. Wszystkie metody klasy Przestrzen które operują na tablicy są synchronizowane, a zadania takie jak wykonanie kroku polegające na zmianie swojej pozycji i zwolnieniu poprzedniej zamknięte są w pojedyncze metody, aby tablica nie utraciła spójności.

Problem polega jednak na tym, że pomimo tego wszystkiego obiekty "wchodzą na siebie". Nie umiem stwierdzić czy problem leży po stronie logiki programu (i synchronizacji) czy może np. po stronie GUI?

Poniżej zamieszczam możliwe najkrótszy kod czterech klas, aby zaprezentować sytuację.
Utworzona plansza ma wymiar 3x2 i tworzę 5 obiektów, zatem zawsze dokładnie tylko jedno pole powinno być wolne.

Klasa Glowna

import javax.swing.JFrame;
import javax.swing.SwingUtilities;


public class Glowna extends JFrame {

    Rysowanie mojPanel;
    
    public Glowna() {
        
        initUI();
        
    }
    
    public void initUI() {
        
        mojPanel = new Rysowanie();
        getContentPane().add(mojPanel);
        
        setTitle("Przyklad");
        setSize(300, 300);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
        
        new Thread(mojPanel).start();
        
    }
    
    public static void main(String[] args) {
        
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                
                Glowna obj = new Glowna();
                
            }            
            
        });
        
    }
    
}

Klasa Rysowanie:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;


public class Rysowanie extends JPanel implements Runnable{
    
    int pozycja;
    Przestrzen przestrzen;
    
    public Rysowanie() {
        
        przestrzen = new Przestrzen(3, 2);
        
        for (int i=0; i < 5; i++) {
            new Kura(this, przestrzen);
        }
        
    }
    
    public void nowaPozycja(int pozycja) {
        
        this.pozycja = pozycja;
        System.out.println(pozycja);
    }
    
    public void paintComponent (Graphics g) {
        
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        
        int kodObiektu;
        System.out.println(przestrzen.y);
        for (int i=0; i < przestrzen.x; i++) {
            
            for (int j=0; j < przestrzen.y; j++) {
                
                kodObiektu = przestrzen.pobierzKodObiektuZPozycji(i,j);
                
                if (kodObiektu == 0){
                    // tlo
                    g2d.setColor(Color.GRAY);
                    g2d.fillRect(30 * i, 30 * j, 30, 30);                    
                }
                else if (kodObiektu == 1) {
                    // kura
                    g2d.setColor(Color.GREEN);
                    g2d.fillRect(30 * i, 30 * j, 30, 30);                     
                }    


            }
            
        }

    }

    @Override
    public void run() {
        
        while(true) {
            
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            repaint();
            
        }
        
    }
    
}

Klasa Przestrzen:

import java.util.Random;


public class Przestrzen {
    
    int x;
    int y;
    
    int plansza[][];
    
    Random generator;
    
    public Przestrzen(int x, int y) {
        
        this.x = x;
        this.y = y;
        
        plansza = new int[x][y];
        generator = new Random();
    }
    
    
    public synchronized int[] utworzObiektNaPlanszy(int typObiektu) {
        
        int pozycjaX = -1;
        int pozycjaY = -1;
        
        boolean wolneMiejsce = false;
        
        while(!wolneMiejsce) {
            
            pozycjaX = generator.nextInt(x);
            pozycjaY = generator.nextInt(y);
            
            if (plansza[pozycjaX][pozycjaY] == 0) {
                wolneMiejsce = true;
            }
            
        }
        
        int uzyskanaPozycja[] = new int[2];
        uzyskanaPozycja[0] = pozycjaX;
        uzyskanaPozycja[1] = pozycjaY;
        
        
        // Po znalezieniu wolnego miejsca obiekt jest umieszczany na planszy
        plansza[pozycjaX][pozycjaY] = typObiektu;
        
        
        return uzyskanaPozycja;
        
    }
    
    
    
    public synchronized int pobierzKodObiektuZPozycji(int x, int y) {
        
        return plansza[x][y];
        
    }
    
    
    public synchronized int[] wykonajKrokNaPlanszy(int typObiektu, int[] poprzedniaPozycja) {
        
        int poprzedniX = poprzedniaPozycja[0];
        int poprzedniY = poprzedniaPozycja[1];
        
        int nowaPozycja[] = new int[2];
        
        // przepisanie poprzednich wartosci, ktore w razie powodzenia zostana nadpisane
        nowaPozycja[0] = poprzedniX;
        nowaPozycja[1] = poprzedniY;
        
        int kierunek = generator.nextInt(4);
        
        /*
         * 0 - gora
         * 1 - prawo
         * 2 - dol
         * 3 - lewo
         */
        
        if (kierunek == 0) {        
            
            if (poprzedniX - 1 >= 0){
                nowaPozycja[0]--;
            }
            
        }
        else if (kierunek == 1) {
            
            if (poprzedniY + 1 < y) {
                nowaPozycja[1]++;
                
            }
            
        }
        else if (kierunek == 2) {
            
            if (poprzedniX + 1 < x) {
                nowaPozycja[0]++;
            }
            
        }
        else if (kierunek == 3) {
            
            if (poprzedniY - 1 >= 0) {
                nowaPozycja[1]--;
            }
        }
        
        // Zwolenienie poprzedniego miejsca
        plansza[poprzedniX][poprzedniY] = 0;
        
        // Przeniesienie na nowa pozycje
        plansza[nowaPozycja[0]][nowaPozycja[1]] = typObiektu;

        
        return nowaPozycja;
        
    }
    
}

Klasa Kura:

import java.util.Random;

public class Kura extends Thread {
    
    Rysowanie panel;
    Przestrzen przestrzen;
    
    int[] wlasnaPozycja;

    public Kura(Rysowanie panel, Przestrzen przestrzen) {
        
        this.panel = panel;
        this.przestrzen = przestrzen;
        
        wlasnaPozycja = new int[2];
        wlasnaPozycja = przestrzen.utworzObiektNaPlanszy(1);
        System.out.println(wlasnaPozycja[0] + "=" + wlasnaPozycja[1]);
        
        start();
        
    }
    
    public void run() {
        
        while(true) {
        
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            wlasnaPozycja = przestrzen.wykonajKrokNaPlanszy(1, wlasnaPozycja);
            
            System.out.println("Watek uruchomiony");

        
        }
        
    }
    
    
}

0

Błąd masz w metodzie paintComponent, bo chyba nie przyszło ci do głowy ze obiekty mogą zmieniać pozycje w trakcie wykonywania tej twojej pętli rysującej...
Ale ogólny komentarz do całego kodu: przepisz to od nowa. Co zrobisz kiedy obiektów będzie 1000? Rozwiniesz dalej tego swojego ifa ma 1000 przypadków? Jedyna klasa która powinna przechowywać informacje o tym jak namalować kurę to kura, albo ewentualnie jakiś jej składnik. Canvas sam w sobie nie powinien ograniczać typów obiektów do namalowania, bo i czemu?

0

pozycje "kury" masz niezabezpieczoną ponieważ może wygenerować większe liczby jak obszar jpanelu, według mnie troche przekombinowane podejście do sprawy.

0

Dziękuje za cenne uwagi.
W Javie jestem dopiero początkujący, dlatego jeszcze z wieloma rzeczami mam problem.

Zatem w jaki najlepszy i najbardziej uniwersalny sposób zrealizować to rysowanie, jeśli rozwiązanie z tą pętlą jest złe?
Na planszy poruszać się będą jeszcze 2 inne typy obiektów. W dodatku na próbę objąłem wnętrze funkcji paintComponent w blok:

synchronized(przestrzen) {

   // kod funkcji

}

ale to jednak w żaden sposób nie pomogło rozwiązać problemu nachodzenia na siebie obiektów

0

Na prawdę nikt nie jest mi w stanie udzielić wskazówki jak synchronizować tę metodę? :(

0

Ok, to w taki razie chociaż przedstawić model jak to powinno wyglądać. Albo chociaż podać adres do czegoś podobnego gdzie występują jednocześnie wątki, operacje na tablicach i odrysowanie, gdyż nie potrafię znaleźć niczego co by mi pomogło. Tutoriale o tych poszczególnych zagadnieniach przeczytałem, ale wdzięczny byłbym za podanie praktycznego przykładu.

0

odczyt tablicy ruch i odrysowanie musi się odbyć w taki sposób żeby jednocześnie inny typ obiektu nie miał szans tego zrobić

stwórz sobie metodę której argumentem będzie typ obiektu i która będzie robiła te 3 rzeczy i ją zsynchronizuj.

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