Hilos I

Bueno. Hay otras cosas que si que creo que hay que explicar, pero cualquiera que haya llegado hasta aquí tendrá un concepto mínimo de lo que son los hilos. Si imaginamos la ejecución de un programa como una cola de sentencias que esperan pacientemente su turno para pasar por el procesador. Si, eso es… Imagínate que en el súper hay sólo una caja y todos los clientes tienen que esperar con o sin paciencia su turno de cola. Te puede pasar que te encuentres al pesado de turno que se está dos horas ligando con la cajera (el procesador) y que retiene la cola. Entonces lo mejor que se puede hacer es… Abrir otra caja. Pues es exactamente eso. la idea de los múltiples hilos es habilitar la posibilidad al programador de abrir otro hilo de ejecución (otra caja con otra cola). Gracias a las maravillas de Java no te tienes que preocupar por eso. Tu le dices que abra otro hilo de ejecución y listo. De la faena sucia se encarga como siempre el motor de Java.

Mira el ejemplo siguiente (TE ADVIERTO QUE LA ÚNICA MANERA DE SALIR ES CON CONTROL - C). Como que tan sólo hay un hilo de ejecución. Cuando entramos en el bucle infinito. El programa se queda lerdo y ya no responde a nada. Y en un entorno de Interfaz de Usuario es una cagada. El usuario nunca tiene paciencia para que acabe el proceso que está ocupando el procesador. Y por elegancia y sentido común al menos el botón de salir debería estar libre siempre de estos problemas :

//Contador1.java
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import net.juantxu.swing.*;

public class Contador1 extends JApplet{
	private int contador = 0;
	private JButton empezar = new JButton("Empezar"),
			onOff = new JButton("Conmutar");
	private JTextField t = new JTextField(10);
	private boolean chivato = true;
	public void init(){
		Container cp = getContentPane();
		cp.setLayout(new FlowLayout() );
		cp.add(t);
		empezar.addActionListener(new EmpezarL() );
		cp.add(empezar);
		onOff.addActionListener(new OnOffL() );
		cp.add(onOff);
	}
	public void comenzar(){
		while(true){
			try{
				Thread.sleep(100);
			}catch(InterruptedException e){
				System.err.println("interrumpido");
			}
			if(chivato) t.setText(Integer.toString(contador++));
		}
	}
	class EmpezarL implements ActionListener{
		public void actionPerformed(ActionEvent e){
			comenzar();
		}
	}
	class OnOffL implements ActionListener{
		public void actionPerformed(ActionEvent e){
			chivato = !chivato;
		}
	}
	public static void main(String[] args){
		Console.run(new Contador1(), 300, 100);
	}
}

si lo quieres ver está aqui (Sólo se puede cerrar matando el proceso Control -C)

Para evitar esto están los Hilos (Threads). Lo dicho, habilitar varias cajas de cobro. Lo más fácil y comun es hacer una clase que hereda de Thread y después sobreescribes el método run() que es el que dice el código que se ejecutará. Una vez hecho eso, el se empezará a ejecutar cuando le digas start().

Vamos a ver como va con un ejemplo simple:

//HilosSimples.java
public class HilosSimples extends Thread{
	private int cuentaAtras = 10;
	private static int contador=0;
	private int numeroHilo = contador++;
	public HilosSimples(){
		System.out.println("Creando "+ numeroHilo );
	}
	public void run(){
		while (true){
			System.out.println("Hilo "+ numeroHilo + "(" +  cuentaAtras + ")" );
			if(--cuentaAtras == 0) return;
		}
	}
	public static void main(String[] s){
		for(int i = 0; i<5; i++){
			new HilosSimples().start();
		}
		System.out.println("Todos los hilos arrancados");
	}
}

Como ves se van en el main vamos creando nuevas instancias de hilos simples, que en realidad son nuevo hilos( hasta 5 hilos) y le decimos start() a partir de ahi cada hilo ejecuta lo suyo (lo que le decimos que haga en run() )

Vamos a reescribir el ejemplo del principio, el que se cuelga con esta estrategia de hilos. Verás como, al meter el bucle infinito en un una subclase que abre un nuevo hilo, esto nos permite que el resto del programa siga su ejecución normal y que responda de una manera satisfactoria para el usuario

//Contador2.java
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import net.juantxu.swing.*;

public class Contador2 extends JApplet{
	private class SubtareaSeparada  extends Thread{
		private int contador = 0;
		private boolean chivato = true;
		SubtareaSeparada(){ start(); }
		void invertirChivato(){chivato = !chivato;}
		public void run(){
			while(true){
				try{
					Thread.sleep(100);
				}catch(InterruptedException e){
					System.err.println("interrumpido");
				}
			if(chivato) t.setText(Integer.toString(contador++));
			}
		}

	}
	private SubtareaSeparada sp = null;
	private JButton empezar = new JButton("Empezar"),
			onOff = new JButton("Conmutar");
	private JTextField t = new JTextField(10);

		public void init(){
			Container cp = getContentPane();
			cp.setLayout(new FlowLayout() );
			cp.add(t);
			empezar.addActionListener(new EmpezarL() );
			cp.add(empezar);
			onOff.addActionListener(new OnOffL() );
			cp.add(onOff);
	}
	
	class EmpezarL implements ActionListener{
		public void actionPerformed(ActionEvent e){
			if (sp==null){
				sp = new SubtareaSeparada();
			}
		}
	}
	class OnOffL implements ActionListener{
		public void actionPerformed(ActionEvent e){
			if(sp != null){
				sp.invertirChivato();
			}
		}
	}
	public static void main(String[] args){
		Console.run(new Contador2(), 300, 100);
	}
}

miralo aqui

Existe tambien otra forma de crear hilos que es la interfaz Runnable aunque yo personalmente la desaconsejo por poco clara. Es mucho mas fácil y legible la forma anterior de las clases internas. El truco de Runnable es que permite crear un hilo al que se le pasa la propia clase. Te pongo un ejemplo para que veas como va, pero mejor te quedas con el otro sistema.

//Contador3.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import net.juantxu.swing.*;

public class Contador3 extends JApplet implements Runnable{
	private int contador = 0 ;
	private boolean chivato = true;
	private Thread hiloPropio = null;
	private JButton 
			empezar = new JButton("empezar"),
			onOff = new JButton("conmutar");
	private JTextField t = new JTextField(10);
	public void run(){
		while(true){
			try{
				hiloPropio.sleep(100);	
			}catch(InterruptedException e){
				System.err.println("interrumpido");
			}
			if(chivato) t.setText(Integer.toString(contador++));
		}

	}
	class EmpezarL implements ActionListener{
		public void actionPerformed(ActionEvent e){
			if(hiloPropio==null){
				hiloPropio = new Thread(Contador3.this);
				hiloPropio.start();
			}
		}
	}
	class OnOffL implements ActionListener{
		public void actionPerformed(ActionEvent e){
			chivato = !chivato;
		}
	}
	public void init(){
		Container cp = getContentPane();
		cp.setLayout(new FlowLayout());
		cp.add(t);
		empezar.addActionListener(new EmpezarL());
		cp.add(empezar);
		onOff.addActionListener(new OnOffL());
		cp.add(onOff);	
	}
	public static void main(String[] s){
		Console.run(new Contador3(), 300,200);
	}

}

Y aqui tienes un ejemplo ce como crear y gestionar tantos hilos como quieras. Para facilitar el manejo se ha creado un array de hilos llamado Teletiopo que contiene todos los hilos que queramos crear. Si lo ejecutamos desde una web cogerá el número de hilos del parámetro “tamanio” y si lo ejecutamos desde consola lo cogerá del parámetro que le pasemos al ejecutarlo.

//Contador4.java
  import javax.swing.*;
  import java.awt.*;
  import java.awt.event.*;
  import net.juantxu.swing.*;

public class Contador4 extends JApplet{
	private JButton empezar = new JButton("empezar");
  	private boolean empezado = false;
	private Teletipo[] s;
	private boolean esApplet = true;
	private int tamanio=12;
	class Teletipo extends Thread{
		private JButton b = new JButton("conmutar");
		private JTextField t = new JTextField(5);
		private int contador  = 0;
		private boolean chivato = true;
		public Teletipo(){
			b.addActionListener(new ConmutadorL() );
			JPanel p = new JPanel();
			p.add(t);
			p.add(b);
			// invoca JApplet.getContentPane().add()
			getContentPane().add(p);
		}
	
		class ConmutadorL implements ActionListener{
			public void actionPerformed(ActionEvent e){
				chivato = !chivato;
			}
		}
		public void run(){
			while(true){
				if(chivato) t.setText(Integer.toString(contador++));
				try{
					sleep(100);
				}catch(InterruptedException e){
					System.err.println("interrumpido");
				}
			}
		}
	}
	class EmpezarL implements ActionListener{
		public void actionPerformed(ActionEvent e){
			if(!empezado){
				empezado = true;
				for (int i = 0; i< s.length; i++) s[i].start();
			}
		}
	}
	public void init(){
		Container cp = getContentPane();
		cp.setLayout(new FlowLayout() );
		// cpaturar el parmetro del tamaño de la web
		if(esApplet){
			String tam = getParameter("tamanio");
			if(tam != null) tamanio = Integer.parseInt(tam);
		}
		s = new Teletipo[tamanio];
		for (int i = 0; i< s.length; i++) s[i]= new Teletipo();
		empezar.addActionListener(new EmpezarL());
		cp.add(empezar);
	}
	public static void main(String[] args){
		Contador4 applet = new Contador4();
		// esto no es un applet por lo que se pone el flag a 1 y se producen los valores de patrametros para args
		applet.esApplet = false;
		if(args.length != 0) applet.tamanio = Integer.parseInt(args[0]);
		Console.run(applet, 200, applet.tamanio  * 50 );
	}
}

y queda algo parecido a esto si quieres bajarte el .jar y ejecutarlo desde consola lo tienes aqui. para ejecutar es:

java -jar C4.jar 8
 
java/35.txt · Última modificación: 2008/05/13 10:00 (editor externo)
 
Excepto donde se indique lo contrario, el contenido de esta wiki se autoriza bajo la siguiente licencia:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki