Listado completo de tutoriales
101 - Hilos en Java - problemas de aplicación |
En el concepto anterior vimos para que sirven los hilos y como se implementan los mismos en Java. Ahora veremos algunos ejemplos que se ven favorecidos empleando hilos.
Desarrollar un programa que cargue en un objeto de la clase 'JTextArea' todos los directorios y archivos de la unidad "C". Como podemos imaginar esta es una actividad larga y lenta, por lo que si no la hacemos dentro de un hilo nuestro programa no responderá a las instrucciones del operador durante varios minutos.
Dispondremos de un botón para finalizar el programa en cualquier momento.
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; public class FormularioBusqueda extends JFrame implements ActionListener { JTextArea textarea1; JScrollPane scrollpane1; JButton boton1; FormularioBusqueda() { setLayout(null); textarea1 = new JTextArea(); scrollpane1 = new JScrollPane(textarea1); scrollpane1.setBounds(10, 30, 760, 300); add(scrollpane1); boton1 = new JButton("Salir"); boton1.addActionListener(this); boton1.setBounds(320, 350, 100, 30); add(boton1); textarea1.setText(""); HiloBusqueda hb = new HiloBusqueda("c:\\", textarea1); hb.start(); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == boton1) System.exit(0); } public static void main(String[] arguments) { FormularioBusqueda fb; fb = new FormularioBusqueda(); fb.setBounds(0, 0, 800, 640); fb.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); fb.setVisible(true); } } class HiloBusqueda extends Thread { String directorio; JTextArea ta; public HiloBusqueda(String directorio, JTextArea ta) { this.directorio = directorio; this.ta = ta; } @Override public void run() { leer(directorio); } private void leer(String inicio) { File ar = new File(inicio); String[] dir = ar.list(); if (dir != null) for (int f = 0; f < dir.length; f++) { File ar2 = new File(inicio + dir[f]); if (ar2.isFile()) ta.append(inicio + dir[f] + "\n"); if (ar2.isDirectory()) { ta.append(inicio + dir[f].toUpperCase() + " --> [Directorio]\n"); leer(inicio + dir[f] + "\\"); } } } }
Cuando ejecutamos el programa podemos ver como se van cargando todos los archivos y directorios de la unidad "C:\", pero en cualquier momento podemos presionar el botón de "Salir" ya que la búsqueda se hace en otro hilo al principal de la aplicación:
Desde el constructor de la clase 'FormularioBusqueda' creamos un objeto de la clase 'HiloBusqueda', pasando la carpeta del disco duro a recorrer y el objeto de la clase JTextArea donde mostrar los datos:
HiloBusqueda hb = new HiloBusqueda("c:\\", textarea1);
Finalmente llamamos al método 'start' para inicializar el hilo:
hb.start();
La clase 'HiloBusqueda' define como atributos el directorio a recorrer y el JTextArea donde mostrar los datos, dichos atributos se inicializan en el constructor:
class HiloBusqueda extends Thread { String directorio; JTextArea ta; public HiloBusqueda(String directorio, JTextArea ta) { this.directorio = directorio; this.ta = ta; }
El método 'run' se llama luego que desde la otra clase llamamos al método 'start'. En el método 'run' llamamos al método recursivo 'leer' para visitar todas las carpetas y archivos que hay en el directorio indicado en el atributo 'directorio':
@Override public void run() { leer(directorio); }
Dentro del método leer lo primero que hacemos es crear un objeto de la clase 'File' y mediante el metodo 'list' recuperamos todos los archivos y carpetas del directorio donde nos encontramos posicionados:
private void leer(String inicio) { File ar = new File(inicio); String[] dir = ar.list();
Si la variable dir almacena un null significa que se trata de un directorio protegido y no tenemos acceso al mismo:
if (dir != null)
Ahora mediante un for recorremos el vector con todos los nombres de archivos y carpetas:
for (int f = 0; f < dir.length; f++) {
Por cada archivo y carpeta creamos un objeto de la clase File para verificar de que tipo de recurso se trata (archivo o carpeta):
File ar2 = new File(inicio + dir[f]);
Si es un archivo lo agregamos al 'JTextArea':
if (ar2.isFile()) ta.append(inicio + dir[f] + "\n");
Si es un directorio además de agregarlo al 'JTextArea' procedemos a llamar en forma recursiva para recorrer la subcarpeta:
if (ar2.isDirectory()) { ta.append(inicio + dir[f].toUpperCase() + " --> [Directorio]\n"); leer(inicio + dir[f] + "\\"); }
Si codificamos el mismo programa sin emplear el concepto de hilos podremos comprobar que el JFrame no aparece en pantalla hasta que finaliza el recorrido completo del disco duro (recorremos la carpeta 'c:\windows' en lugar de 'c:\' para que no demore tanto tiempo):
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; public class FormularioBusqueda extends JFrame implements ActionListener { JTextArea textarea1; JScrollPane scrollpane1; JButton boton1; FormularioBusqueda() { setLayout(null); textarea1 = new JTextArea(); scrollpane1 = new JScrollPane(textarea1); scrollpane1.setBounds(10, 30, 760, 300); add(scrollpane1); boton1 = new JButton("Salir"); boton1.addActionListener(this); boton1.setBounds(320, 350, 100, 30); add(boton1); textarea1.setText(""); Busqueda hb = new Busqueda("c:\\windows\\", textarea1); hb.iniciar(); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == boton1) System.exit(0); } public static void main(String[] arguments) { FormularioBusqueda fb; fb = new FormularioBusqueda(); fb.setBounds(0, 0, 800, 640); fb.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); fb.setVisible(true); } } class Busqueda { String directorio; JTextArea ta; public Busqueda(String directorio, JTextArea ta) { this.directorio = directorio; this.ta = ta; } public void iniciar() { leer(directorio); } private void leer(String inicio) { File ar = new File(inicio); String[] dir = ar.list(); if (dir != null) for (int f = 0; f < dir.length; f++) { File ar2 = new File(inicio + dir[f]); if (ar2.isFile()) ta.append(inicio + dir[f] + "\n"); if (ar2.isDirectory()) { ta.append(inicio + dir[f].toUpperCase() + " --> [Directorio]\n"); leer(inicio + dir[f] + "\\"); } } } }
Luego de ejecutar el programa anterior sin hilos podremos afirma que el empleo de hilos para recorrer el disco duro es una herramienta fundamental.
Crear un vector con quinientos millones de elementos con valores aleatorios. Proceder a buscar el mayor sin el empleo de hilos, luego buscar el mayor pero utilizando dos hilos, uno que busque el mayor en la primera parte del vector y otro que busque en la segunda parte.
Mostrar la cantidad de tiempo consumido en la búsqueda del mayor con hilos y sin hilos.
import java.util.Date; public class MayorVector { public static void main(String[] args) { System.out.println("Cantidad de núcleos del procesador:" + Runtime.getRuntime().availableProcessors()); int[] v = new int[500_000_000]; System.out.println("Inicio de la carga del vector."); for (int f = 0; f < v.length; f++) v[f] = (int) (Math.random() * 2_000_000_000); System.out.println("Fin de la carga del vector."); Date d1 = new Date(); HiloMayor hilo1 = new HiloMayor(); hilo1.fijarRango(0, v.length / 2, v); HiloMayor hilo2 = new HiloMayor(); hilo2.fijarRango(v.length / 2 + 1, v.length - 1, v); hilo1.start(); hilo2.start(); while (hilo1.isAlive() || hilo2.isAlive()) ; System.out.print("Mayor elemento del vector:"); if (hilo1.may > hilo2.may) System.out.println(hilo1.may); else System.out.println(hilo2.may); Date d2 = new Date(); long milisegundos = (d2.getTime() - d1.getTime()); System.out.println("Milisegundos requeridos con 2 hilos:" + milisegundos); d1 = new Date(); int may = v[0]; for (int f = 1; f < v.length; f++) { if (v[f] > may) may = v[f]; } System.out.println("Mayor elemento del vector:" + may); d2 = new Date(); milisegundos = (d2.getTime() - d1.getTime()); System.out.println("Milisegundos requeridos sin hilos:" + milisegundos); } } class HiloMayor extends Thread { int[] v; int ini, fin; int may; void fijarRango(int i, int f, int[] v) { this.ini = i; this.fin = f; this.v = v; } public void run() { may = v[ini]; for (int f = ini + 1; f < fin; f++) { if (v[f] > may) may = v[f]; } } }
Si la computadora tiene más de un núcleo podemos comprobar que la búsqueda del mayor requiere menos tiempo al emplear dos hilos que se ejecutan en forma paralela:
Podemos saber la cantidad de núcleos de nuestra computadora llamando al método estático 'getRuntime' el cual retorna un objeto de la clase 'Runtime' y mediante este llamamos al método 'availableProcessors':
System.out.println("Cantidad de núcleos del procesador:" + Runtime.getRuntime().availableProcessors());
Creamos dos objetos de la clase HiloMayor y le pasamos los rangos del vector 'v' que deben obtener el mayor:
HiloMayor hilo1 = new HiloMayor(); hilo1.fijarRango(0, v.length / 2, v); HiloMayor hilo2 = new HiloMayor(); hilo2.fijarRango(v.length / 2 + 1, v.length - 1, v); hilo1.start(); hilo2.start();
Luego que llamamos al método 'start' para cada hilo procedemos a quedarnos dentro de un while vacío (por eso es fundamenta el punto y coma al final del while):
while (hilo1.isAlive() || hilo2.isAlive()) ;
El método 'isAlive' retorna true mientras el hilo no ha terminado.
Cuando los dos hilos hay finalizado procedemos a buscar el mayor en el hilo principal del programa y calcular también la cantidad de milisengundos requeridos:
d1 = new Date(); int may = v[0]; for (int f = 1; f < v.length; f++) { if (v[f] > may) may = v[f]; } System.out.println("Mayor elemento del vector:" + may); d2 = new Date(); milisegundos = (d2.getTime() - d1.getTime()); System.out.println("Milisegundos requeridos sin hilos:" + milisegundos);
Como el equipo donde ejecuté el programa tiene más de un núcleo el tiempo requerido con dos hilos paralelos es menor que la búsqueda del mayor en un único hilo.
Si ejecutamos el programa en una computadora con un único núcleo el empleo de hilos hará más ineficiente la búsqueda del mayor del vector.
También podríamos haber utilizado varios hilos para la carga de los valores aleatorios y hacer más rápido su almacenamiento.