NullPointer losowo w GUI

0

Witam. Próbuję znaleźć powód dla którego dostaje losowo NullPointerExcetion w prostym programie. Program ma symulować dwa różne kolory płytek ganiające się za sobą. Jak na razie jest tylko jeden kolor i nie robi nic poza pojawieniem się w losowym miejscu na planszy. Wszystko fajnie, działa ale z jakiegoś powodu wyskakuje ten chory nullpointer. Nie ma zasady, raz wyrzuci raz nie. Zawsze natomiast po utworzeniu wszystkich 6 wątków. Jedyne podejrzenie jakie mam to to, że może problem polega na tym, że szybciej wątek próbuje pokolorować JPanel , niż go stworzyć. Ale jako że dzieje się to w EventQueue to raczej niemożliwe. Ktoś coś ?

MainFrame.java

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;


public class MainFrame extends JFrame{
	JButton Close;
	JButton New;
	JButton Initialize;
	JButton Stop;
	JButton Start;
	
	JPanel ButtonPanel;
	JPanel ViewPanel;
	JPanel RightMargin;
	JPanel LeftMargin;
	JPanel BottomMargin;
	
	JPanel[][] PanelsTab;
	
	public MainFrame(int MapSizeX, int MapSizeY) {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setResizable(false);
		setLocation(500, 200);
		setSize(800, 800);
		setLayout(new BorderLayout());
		
		Close = new JButton("Zamknij");
		New = new JButton("Czysc");
		Initialize = new JButton("Inicjuj");
		Start = new JButton("Start");
		Stop = new JButton("Stop");
		
		ButtonPanel = new JPanel(new FlowLayout());
		ViewPanel = new JPanel(new GridLayout(MapSizeX, MapSizeY, 2, 2));
		
		ButtonPanel.add(Initialize);
		ButtonPanel.add(Start);
		ButtonPanel.add(Stop);
		ButtonPanel.add(New);
		ButtonPanel.add(Close);
		
		PanelsTab = new JPanel[MapSizeX][MapSizeY];
		RightMargin = new JPanel();
		LeftMargin = new JPanel();
		BottomMargin = new JPanel();
		
		for(int i = 0; i < MapSizeX; i++ ) {
			for(int j = 0; j < MapSizeY; j++) {
				PanelsTab[i][j] = new JPanel();
				PanelsTab[i][j].setBackground(Color.WHITE);
				ViewPanel.add(PanelsTab[i][j]);
			}
		}
		
		add(RightMargin, BorderLayout.LINE_END);
		add(LeftMargin, BorderLayout.LINE_START);
		add(BottomMargin, BorderLayout.PAGE_END);
		add(ViewPanel, BorderLayout.CENTER);
		add(ButtonPanel, BorderLayout.NORTH);
		
		Close.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				
			}
		});
		
		setVisible(true);
	}
}

GUI.java

import java.awt.Color;
import java.awt.EventQueue;


public class GUI implements Runnable{
	private int MapSizeX;
	private int MapSizeY;
	private static MainFrame MainFrame1;
	
	public GUI(int x, int y) {
		this.MapSizeX = x;
		this.MapSizeY = y;
	}
	public void run() {
		EventQueue.invokeLater(new Runnable() {
			
			@Override
			public void run() {
				MainFrame1 = new MainFrame(MapSizeX, MapSizeY);
				
			}
		});
	}
	
	public static void InsertRabbit(int x, int y) {
		MainFrame1.PanelsTab[x][y].setBackground(Color.BLUE);
	}
	
	public static void InsertWolf(int x, int y) {
		MainFrame1.PanelsTab[x][y].setBackground(Color.RED);
	}
	
	public static void Clear(int x, int y) {
		MainFrame1.PanelsTab[x][y].setBackground(Color.WHITE);
	}
}

Rabbit.java

import java.awt.EventQueue;


public class Rabbit implements Runnable{
	private int posX;
	private int posY;
	private boolean isAlive;
	private int RabbitNumber;
	
	public Rabbit(int number) {
		this.isAlive = true;
		this.RabbitNumber = number;
		
		int helpX = Simulate.Generator.nextInt(Simulate.MapSizeX);
		int helpY = Simulate.Generator.nextInt(Simulate.MapSizeY);
		
		while(Simulate.Mapa.CheckContent(helpX, helpY) != "Clear") {
			helpX = Simulate.Generator.nextInt(Simulate.MapSizeX);
			helpY = Simulate.Generator.nextInt(Simulate.MapSizeY);
		}
		
		this.posX = helpX;
		this.posY = helpY;
		
		Simulate.Mapa.InstertRabbit(this.posX, this.posY);
		System.out.println("Rabbit number " + RabbitNumber + " has been born on " + (this.posX) + " " + (this.posY) + "!");
		EventQueue.invokeLater(new Runnable()
		{
			public void run() {
				GUI.InsertRabbit(posX, posY);
			}
		});
		
		
	}
	
	private synchronized void Move() {
		while(isAlive) {
			
			try{
				System.out.println("Rabbit number " + this.RabbitNumber  + " is moving!");
				wait(5000);
			}catch(InterruptedException e) {
				System.out.println("Ohh noes, rabbit number " + this.RabbitNumber + " is dead but not in natural way!");
			}
		
		}
	}
	
	public void run(){
		//Move();
	}
}

Wolf.java

public class Wolf implements Runnable{
	
	public void run(){
		
	}
}

Simulate.java

import java.util.Random;


public class Simulate {
	public static int WolfsAmount = 0;
	public static int RabbitsAmount = 0;
	public static int MapSizeX;
	public static int MapSizeY;
	public static Map Mapa;
	public static Random Generator;
	public static Thread[] RabbitsTab;
	public static Thread[] WolfsTab;
	
	public static void main(String[] args) {
		
		if(args.length < 4) {
			System.out.println("Za malo argumentow!");
			return;
		}
		
		try{
			MapSizeX = Integer.parseInt(args[0]);
			MapSizeY = Integer.parseInt(args[1]);
			WolfsAmount = Integer.parseInt(args[2]);
			RabbitsAmount = Integer.parseInt(args[3]);
			
			if(MapSizeY < 2 || MapSizeX < 2 ||WolfsAmount < 1 || RabbitsAmount < 1) {
				System.out.println("Nieprawidlowe dane startowe!");
				return;
			}
			
			Generator = new Random();
			Mapa = new Map(MapSizeX, MapSizeY);
			GUI GuiT = new GUI(MapSizeX, MapSizeY);
			Thread GUIThread = new Thread(GuiT);
			GUIThread.start();
			
			
			
			RabbitsTab = new Thread[RabbitsAmount];
			WolfsTab = new Thread[WolfsAmount];
			
			for(int i = 0; i < RabbitsAmount; i++) {
				RabbitsTab[i] = new Thread(new Rabbit(i + 1));
				RabbitsTab[i].start();
			}
			
			
			
		}catch(NumberFormatException e){
			System.out.println("Dane startowe nie sa liczbami!");
		}
		
		
		
	}
}

Map.java

public class Map {
	private String[][] Content;
	private int height;
	private int width;
	
	public Map(int x, int y){
		this.height = x;
		this.width = y;
		
		this.Content = new String[x][y];
		
		for(int i = 0; i < x; i++) {
			for(int j = 0; j < y; j++) {
				this.Content[i][j] = "Clear";
			}
		}
	}
	
	public void Clear(int x, int y) throws Exception{
		if(x < 0 || x > this.height - 1 || y < 0 || y > this.width -1) {
			throw new Exception("Out of range!");
		}
		
		this.Content[x][y] = "Clear";
	}
	
	public void ClearAll() {
		for(int i = 0; i < this.height; i++) {
			for(int j = 0; j < this.width; j++) {
				this.Content[i][j] = "Clear";
			}
		}
	}
	
	public void InsertWolf(int x, int y) {
		this.Content[x][y] = "Wolf";
	}
	
	public void InstertRabbit(int x, int y) {
		this.Content[x][y] = "Rabbit";
	}
	
	public String CheckContent(int x, int y) {
		return this.Content[x][y];
	}
}
0

Nie chciało mi się wgłębiać, ale: EventQueue.invokeLater() tworzy wątek rozdzielający wydarzenia. Który jest jeden i powinien "obejmować" cały program żeby, no właśnie, obsługiwać wydarzenia. Jeżeli tworzysz nowy wątek rozdzielający wydarzenie, to zastępujesz stary. Resztę synchronizacji na zasobach musisz wykonać sam. prześledź jeszcze raz ścieżkę wywołań. Jeżeli błąd wyskakuje losowo to albo jest zależny od jakiegoś mechanizmu losującego (w co wątpię) albo jakieś wątki zaczynają odwoływać się do niezsynchronizowanych zasobów kiedy nie powinny tego robić.
I, na Boga, pisz nazwy zmiennych i metod z małej litery. Są pewne zasady normalizacji, zwłaszcza jeżeli chcesz żeby ktoś czytał twój kod.

0

Jak ci leci wyjątek, to po pierwsze powinieneś ustalić i pokazać skąd on leci (najlepiej całego call stack-a).

0

Jeżeli tworzę 6 czerwonych JPaneli, to próba pokolorowania ostatniego kończy się wyjątkiem. Problem leży w EventQueue bo szybciej koloruje niż tworzę (trochę to idiotyczne według mnie ale widać jest możliwe) . Pomogło generowanie na starcie czystych paneli a losowe kolorowanie ich dopiero pod buttonem (przypuszczam że gdybym był w stanie dość szybko wcisnąć button to i tak poleciałby wyjątek) co nie koniecznie rozwiązuje problem, jedynie go omija. Nie mogę skumać jak to zrobić żeby za całe GUI odpowiadał jeden wątek i sam sobie w paradę nie wchodził, do tego robiąc to w odpowiedniej kolejności.

0

Nigdzie nie napisałem, że za całe gui ma odpowiadać jeden wątek, jedynie, że za obsługę wydarzeń ma odpowiadać jeden wątek.

public static void main(String... a){
		EventQueue.invokeLater(new Runnable(){
			@Override
			public void run(){
				new MainClass(); //główne okno programu
			}
		});
	}

i już. Wszystkie zdarzenia z głównego okna i wszystkich obiektów które tworzy/zawiera jego klasa będą wykonywane w tym wątku. Twórcy biblioteki Swing zadbali, żeby ta prosta konstrukcja wystarczyła do zapewnienia synchronizacji obsługi wszystkich wydarzeń w programie, nie tylko z bezpośrednio tworzonego w metodzie obiektu, bo chyba w ten sposób ty to rozumiesz.
Sam program może używać dowolnej liczby wątków.

0

Przecież tak mam zrobione właśnie.

0

Co mi tu bajdurzysz. Masz EventQueue.invokeLater() w klasach GUI i Rabbit.

0

No dobrze, ale jak chce żeby inny wątek pokolorował JPanel to nie powinienem tego robić bezpośrednio tylko właśnie EventQueue.invokeLater() o ile się orientuję.

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