Mòdul 8

Pràctica 6: Programar amb fils   
Tornar presentació tema
Pràctica 6 Pràctica 2 Pràctica 3 Pràctica 4 Pràctica 5 Pràctica 1 Pràctica 6  
     
     
  Fils (Threads). El procés continu.  
     
  Programació multitasca:    
     
  Els sistemes operatius actuals són tots multitasca , és a dir, poden executar diferents feines simultànies, i també tenen la capacitat de gestionar programes dissenyats per a llançar diferents fils (seguits de tasques, "threads") considerablement independents els uns dels altres.

Gairebé tots els programes que pots trobar actualment en el mercat són multifil. Estàs acostumats a que un programa pugui fer diverses coses a la vegada i segur que t'irrita, tanmateix, que un procés que no respon et bloquegi l'aplicació i, fins i tot et pengi l'ordinador. Som consumidors de programes multifil i ens hem de convertir en dissenyadors de programació concurrent, com a mínim si volem fer projectes de dimensions mitjanes que transmetin a l'usuari sensació d'agilitat i solidesa.

Java està ben preparat per a la programació en fils. Disposa d'una classe específica, java.lang.Thread, i una interfície, java.lang.Runnable, que et permetran aplicar fils a qualsevol lloc que calgui. No tot són bones notícies, però: com d'altres aspectes de la programació avançada, codificar amb fils no és pas cap tasca intuïtiva. Els fils són objectes delicats i complicats perquè poden tenir preferències d'execució diferents, poden escoltar-se els uns als altres, poden agrupar-se, fer exclusivament de servidors d'altres fils i tantes altres característiques que el seu aprenentatge podria ocupar tot aquest curs

 
     
  El procés continu:    
     
  El procés continu del Joc de la Vida ha de ser un Thread (fil) d'aquests:ha de funcionar ell sol amb independència de si premem un botó o fem qualsevol altra acció. Naturalment, l'acció (esdeveniment!) de prémer el botó "Procés continu: aturar/engegar" ha de fer alguna cosa sobre aquest Thread (fil), però pas cap altra cosa!  
     
  La classe java.lang.Thread, que és la que representa un fil, servirà per a aquesta finalitat. La classe conté, entre d'altres, quatre mètodes que són els importants ara:  
     
 
  • public void start (): Fa que el Thread (fil) comenci a executar-se: la Màquina Virtual de Java, aleshores, crida al mètode run() d'aquest Thread.

  • public void run (): És el mètode cridat en començar l'execució del Thread (fil). Aquest mètode ha de contenir tot allò que vulguis que faci aquest Thread. El mètode run() no s'ha de cridar mai directament: per engegar un Thread cal cridar el mètode start() d'aquest Thread.

  • public static void sleep (long millisegons): Per adormir el Thread la quantitat de mil·lisegons desitjada. L'ús principal (que és el que en faràs aquí) és el de controlar la velocitat d'execució dels bucles while o for.

  • public final void setPriority(int prioritat): Per determinar la prioritat d'execució d'aquest Thread davant d'altres Threads. El paràmetre int prioritat ha de ser un dels valors static int MAX_PRIORITY, static int MIN_PRIORITY o static int NORM_PRIORITY.
 
     
Ja pots començar: al projecte JocDeLaVida hi crees la nova classe ProcesContinu, filla de la classe Thread:  
     
/**
 * La classe ProcesContinu representa el procés que resulta de
 * l'encadenament sense solució de continuitat de successius
 * estats del món del "Joc de la Vida".
 *
 * @author (el vostre nom)
 * @version (un número de versió o la data)
 */

public class ProcesContinu extends Thread {

    /**
     * La Finestra en la qual s'esdevé aquest ProcésContinu.
     */

    protected Finestra finestra;

 
   /**
     * Mètode constructor per a objectes de la classe
     * ProcesContinu.
     * @param finestra la Finestra en la qual s'esdevé aquest
     * ProcésContinu.
     */

    public ProcesContinu(Finestra finestra) {
// constructor
        this.finestra=finestra;
        setPriority(Thread.MIN_PRIORITY);
    }

}

 
     
  Observa que, per començar, al fil ProcesContinu li dónes prioritat mínima, per tal que no empipi encara...  
     
  Ara cal que la classe Finestra construeixi efectivament el fil ProcesContinu i el posi en marxa. A la classe Finestra has d'afegir-li aquest codi:  
     
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

/**
* La classe Finestra és la classe arrel de l'aplicació del Joc
* de la Vida.
*
* @author (el vostre nom)
* @version (un número de versió o la data)
*/
public class Finestra extends JFrame {

    /**
     * L'objecte Mon en el qual viuen i moren els éssers.
     */
    protected Mon mon;

    /**
     * L'objecte MonVisible que fa visibles els éssers
     * del món.
     */
    protected MonVisible monVisible;

    /**
     * Mètode getter per a la variable mon
     * @return el Mon d'aquesta finestra
     */
    public Mon getMon () {
        return mon;
    }

    /**
     * Mètode getter per a la variable monVisible
     * @return el MonVisible d'aquesta finestra
     */
    public MonVisible getMonVisible () {
        return monVisible;
    }

    /**
     * Botó "Pas a pas"
     */
    private JButton botoPasAPas;

    /**
     * El procés continu
     */

    private ProcesContinu procesContinu;

    /**
     * Botó pel comanament del procés continu
     */
    private JButton botoProcesContinu;

    /**
     * Etiquetes alternatives pel botó d'engegar i parar el
     * procés continu
     */
    private String[] etiquetes={"Engegar procés continu",
                                "Parar procés continu"};

    /**
     * Estat d'activitat del procés continu.
     */
    boolean esProcesActiu=false;

    /**
     * Mètode constructor per a objectes de la classe Finestra.
     */
    public Finestra () { // constructor
        setConfiguracio();
        setControls();
        mon=new Mon();

        procesContinu=new ProcesContinu(this);
        procesContinu.start();
        inicia();
    }

    <etc.>

 
     
  Ara has de fer que el botó "Engegar/parar procés continu" faci el que ha de fer: simplement establir la prioritat del ProcesContinu. Al mètode setControls() de la classe Finestra afegeix-li aquest codi:  
     
<codi anterior>

// Col·locació del botó "Procés continu"
botoProcesContinu=new JButton(); // Creem el botó
afegeixBoto(botoProcesContinu,panelBotons,etiquetes[0],
            new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                esProcesActiu=!esProcesActiu;
                botoPasAPas.setEnabled(!esProcesActiu);
                    if (esProcesActiu) {
                        botoProcesContinu.setText(etiquetes[1]);

                        procesContinu.setPriority(
                                               Thread.MAX_PRIORITY);
                    } else {
                        botoProcesContinu.setText(etiquetes[0]);

                        procesContinu.setPriority(
                                               Thread.MIN_PRIORITY);
                    }
            }
            });


<etc.>
 
     
  Ja eta ben a prop del final! Només cal escriure el mètode run() de la classe ProcésContinu:, però, abans, et proposem un exercici:  
     
Exercici: per què no funciona?    
     
  Compila el projecte i crea un objecte Finestra. Tens la finestra de l'aplicació davant teu: ara prem el botó "Engegar/parar procés continu". Uiiiiiii! Per què Java se t'enfada tant i tant?  
     
  El final del projecte...    
     
  Bé, tornem al mètode run(): has de tancar tota la seva activitat en un bucle infinit: la clàusula adequada és  
     
 

    while (true) {
        <...codi...>
    }   

 
     
  i com que el botó "Engegar/parar procés continu" canvia la prioritat d'aquest Thread, faràs servir això per distingir si el ProcesContinu ha de fer coses o no:  
     
 

    while (true) {
            if (getPriority()>Thread.MIN_PRIORITY) {
                <...accions a fer...>
            }
    }   

 
     
  Les accions a fer són simplement que el mon faci un pas() i que el monVisible es repinti. Per tal de controlar la velocitat dels esdeveniments, després d'això cal posar a dormir el procesContinu uns quants mil·lisegons: 100 ja estarà bé. El mètode run() de la classe ProcesContinu queda així:  
     
 
<codi anterior>

    /**
     * Mètode run() per a objectes de la classe
     * ProcesContinu.
     */

    public void run () {
        Mon mon=finestra.getMon();
        MonVisible monVisible=finestra.getMonVisible();
            while(true) {
                    if (getPriority()>Thread.MIN_PRIORITY) {
                        mon.pas();
                        monVisible.repaint();
                            try {
                                sleep(100);
                            } catch (In