Wątki z niezalecanymi metodami.

0

Witam

Zrobiłem sobie programik na zaliczenie do szkoły, taka symulacja z przymrużeniem oka układu planetarnego. Każda planeta porusza się w osobnym wątku, który można zatrzymać i wznowić.
W sumie działa ale obsługa wątków działa na zakazanych metodach suspend() i resume(), przy użyciu innych metod wyskakiwał bład w stylu IllegalMonitorStateException, próbowałem coś się o tym dowiedzieć (synchronized itd) ale trudno mi to rozkminić, może mi ktoś wytłumaczyć jak przerobić ten programik aby działał z aktualnie dozwolonymi metodami. Wszelkie uwagi związane nie tylko z wątkami mile widziane.

Wyziew koda:

import se.datadosen.component.*;

public class UniverseFrame extends JFrame {
    
    private PlanetComponent comp;
    private Planet p1, p2, p3, p4, p5, p6, p7, p8, p9;
    private PlanetRunnable r1, r2, r3, r4, r5, r6, r7, r8 ,r9;
    private Thread t1, t2, t3, t4, t5, t6, t7, t8, t9;
    private PlanetMenu merkury, wenus, ziemia, mars, jowisz, saturn, uran, neptun, pluton;
    
    
    public UniverseFrame() {        
        
        setTitle("Wielowatkowe Planety");
        setSize(800, 625);
        setBackground(new Color(38, 18, 112));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new RiverLayout());
        setResizable(false);
        createAndShowGUI();
        setPlanet();
        addActions();
    }
    
    public void createAndShowGUI() {
        
        comp = new PlanetComponent();
        add("left", comp);
        JPanel panelPrawy = new JPanel();
        panelPrawy.setPreferredSize(new Dimension(200, 600));
        
       //tworzenie paneli do obslugi planet (wnow, zatrzymaj)
        merkury = new PlanetMenu("Merkury");
        wenus = new PlanetMenu("Wenus");
        //itd
        
        add("center", panelPrawy);
        panelPrawy.add(merkury);
        panelPrawy.add("br", wenus);
       //itd
  }
   public void setPlanet() {
  
       p1 = new Planet(300, 300, 80, 5);
       comp.add(p1/* 241, 229, 32*/);
       r1 = new PlanetRunnable(comp, p1, 0.0057, 50.0);
       t1 = new Thread(r1);
       t1.start();

//itd
       
}
   
   public void addActions() {
       
        StartAction a1 = null, a2=null, a3=null, a4=null, a5=null, a6=null, a7=null, a8=null, a9=null ;
        StopAction s1=null, s2=null, s3=null, s4=null, s5=null, s6=null, s7=null, s8=null, s9=null;
        newActions(a1, s1, t1, merkury);
        newActions(a2, s2, t2, wenus);
        //itd

   }
   
   public void newActions(StartAction a1, StopAction a2, Thread t, PlanetMenu menu) {
          
          a2 = new StopAction(t, menu);
          a1 = new StartAction(t, menu);
          menu.start.addActionListener(a1);
          menu.stop.addActionListener(a2);
       
   }
   
    
   
  class StopAction implements ActionListener {
    
      private Thread thr;
      private PlanetMenu pm;
      
      public StopAction(Thread t, PlanetMenu pm){
      
          thr=t;
          this.pm= pm;
      }  
      
      public void actionPerformed(ActionEvent e) {
            try {


                pause();
            } catch (InterruptedException ex) {
              
            }
             
        
               pm.start.setEnabled(true);
                ((JButton) e.getSource()).setEnabled(false);
        }
      
      public  void pause() throws InterruptedException{
          //  thr.interrupt();
          thr.suspend();                 //użycie zakazanej metody
         
  }
  }     
  
  
    class StartAction implements ActionListener {
    
      private Thread thr;
      private PlanetMenu pm;
      
      public StartAction(Thread t, PlanetMenu pm){
      
          thr=t;
          this.pm= pm;
      }  
      
      public void actionPerformed(ActionEvent e) {
           

            
                  
                 thr.resume();                           //użycie zakazanej metody
                 pm.stop.setEnabled(true);
                ((JButton) e.getSource()).setEnabled(false);
        }
      
      
  }
    
 }
public class PlanetRunnable implements Runnable {
    
    private double freq;
    private double sleep;
    private Planet p;
    private Component comp;
    
    
    public PlanetRunnable(Component comp, Planet p, double freq, double sleep) {
        
        this.comp = comp;
        this.p = p;
        this.freq = freq;
        this.sleep = sleep;
    }

    public void run() {
        
        
        try {
            
            for (double i=0; i<1000; i+=freq) {
                
                p.move(i);
                comp.repaint();
                Thread.sleep((long) sleep);
                
            }
        } catch (InterruptedException e) {}
        }
 }

Pozdrawiam

0

Sprawa jest dość prosta. Wystarczy rzucić okiem w dokumentację bo tam jest napisane rozwiązanie. Metody typu suspend() powodują fizyczne zatrzymanie przetwarzania wątku przez procesor. Oznacza to tyle, że wątek może być zatrzymany na dowolnej instrukcji, ba nawet w połowie wykonywania tej instrukcji jeżeli nie jest ona atomowa (niepodzielna w kodzie wynikowym). A to oznacza, że wszystkie dane do jakich wątek miał dostęp przed zatrzymaniem należy traktować jako dane uszkodzone - z których nie wolno korzystać aż do wznowienia działania wątku. Tak naprawdę, jednak niczego nie można być pewnym w tej sytuacji.

Kod wątku działa zazwyczaj w jakiejś pętli. Przeróbka kodu, która pozwoli uniknąć takiej sytuacji polega na tym, żeby mieć pełną kontrolę nad tą pętlą. Na przykład procedurę przerwania wątku zamienia się na normalne przerwanie tej pętli (jej warunku) i prawidłowe wyjście z wątku. Natomiast wstrzymanie działania wątku zaimplementować najlepiej jako ustaloną w konkretnym miejscu pułapkę powodującą wejście do pętli testującej warunek jej przerwania. Na przykład takie coś

public volatile boolean koniec_wątku = false;
public volatile boolean pauza = false;
//...
while(!koniec_wątku)
{
  //...
  while(pauza)
    try {TimeUnit.MILISECONDS.sleep(5); } //ziarnistość pauzy do wyskalowania
    catch(InterruptedException e) {}
  //...
}
//koniec wątku

Java gwarantuje, że dostęp do zmiennych boolowskich jest atomowy, a więc można nimi sterować z dowolnego innego wątku bez niepotrzebnych blokad.
Ewentualnie można też użyć zmiennych typu Atomic wraz z leniwym przypisywaniem jeżeli potrzeba więcej bitów do sterowania wątkiem lub bardziej spakowanych.
Taka organizacja wątków gwarantuje, że wątek staje lub jest przerywany dokładnie tam gdzie jest to najbardziej bezpieczne.

ps. W Twoim wypadku kod jest o tyle niebezpieczny, że wywołujesz metodę Swing z innego wątku niż Swing. Powinieneś użyć invokeLater dla operacji move() oraz repaint().
Zauważ też, że sleep też może zostać przerwane i zakomunikowane tym samym wyjątkiem (tworzy przecież osobny wątek). A więc obejmując przez try cały kod medody Run() nigdy się nie dowiesz, czy ktoś zabił Ci wątek tworzony przez sleep() czy wątek Twojej procedury. Zasadniczo stosując try próbuj obejmować blokiem jak najmniej potrzebnego kodu do wyłapania wyjątku.

0

Dzięki za pomocne wyjaśnienie. Pozdrawiam

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