Ufff... veo un montón de clases, cuando lo que se pide no es tanto. No sé si modificar y adaptar ese código es el camino...
Echarle un vistazo para inspirarse tal vez..., pero adaptarlo, puede ser más complicado que comenzar uno nuevo.
Te dejo una propuesta para solucionarlo, a ver si te es útil.
Comenzamos con la clase
Venta.
Se crea una fecha completa para el atributo, pero luego para guardar los datos en el archivo de texto, solo se guarda el año (ignoro mes y día) porque es el dato importante para los posteriores cálculos.
Uso un método que retorna un String con los atributos separados por comas, que serán las líneas que se guardarán en el archivo de texto.
import java.time.LocalDate;
import java.util.Random;
public class Venta {
private String codigo;
private String ciudad;
private LocalDate fecha;
private int valor;
public Venta(int numCodigo) {
codigo = String.format("U%07d", numCodigo); //Formato U0000001
Random azar = new Random();
ciudad = ciudadRandom(azar);
int year = azar.nextInt(5) + 2016; //Año entre 2016 y 2020
int mes = azar.nextInt(12) + 1;
int dia;
if (mes == 2)
dia = azar.nextInt(28) + 1;
else
dia = azar.nextInt(30) + 1;
fecha = LocalDate.of(year, mes, dia);
valor = azar.nextInt(5000) + 1;
}
private String ciudadRandom(Random rnd) {
final String[] ciudades = {"Manta","Portoviejo","Chone","Montecristi","Jipijapa"};
return ciudades[rnd.nextInt(ciudades.length)];
}
public String toTXT() {
//De la fecha solo se guarda el año porque es el dato que importa
return String.format("%s,%s,%d,%d", codigo, ciudad, fecha.getYear(), valor);
}
}
Bien, vamos a ver los hilos.
Hay que crear dos clases que hereden de Thread, es decir, dos tipos de hilo. Cada hilo hará distintos cálculos con los datos del archivo de texto que luego crearemos.
Estos hilos, además de hacer sus cálculos, modificarán el formulario (la interfaz gráfica) mostrando ellos mismos los resultados obtenidos.
No he sabido ver cómo es el formulario del ejercicio de ejemplo. Yo he optado por usar dos áreas de texto, una para cada hilo, y además tendrán una barra de progreso que se incrementarán durante el tiempo que dure el proceso del hilo.
Así que estos hilos se encargarán de leer las líneas del archivo, buscar los datos que necesitan, computar, actualizar la barra de progreso y finalmente mostrar los datos computados en el área de texto.
Para que estos
hilos sepan con cuál barra progreso y área de texto han de interactuar, usaré unos
setter para hacerles llegar las referencias de los correspondientes elementos del formulario con los que han de trabajar.
Esta sería la clase
Hilo1import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
/**
* Suma de valores>3000 de los años 2017, 2018, 2019
*/
public class Hilo1 extends Thread{
private JProgressBar progreso;
private JTextArea texto;
public void setProgreso(JProgressBar progreso) {
this.progreso = progreso;
}
public void setTexto(JTextArea texto) {
this.texto = texto;
}
@Override
public void run() {
progreso.setValue(0);
texto.setText(" Suma de valores > 3000 de los años 2017, 2018, 2019\n"
+ " ----------------------------------------------------------------------------\n\n");
int sumaValores = 0;
File archivo = new File("DatosVentas.txt");
try {
long inicio = System.currentTimeMillis();
BufferedReader lector = new BufferedReader(new FileReader(archivo));
String linea = lector.readLine();
while (linea != null) {
progreso.setValue(progreso.getValue() + 1);
String[] datos = linea.split(",");
int year = Integer.parseInt(datos[2]);
if (year >= 2017 && year <= 2019) {
int valor = Integer.parseInt(datos[3]);
if (valor > 3000)
sumaValores += valor;
}
linea = lector.readLine();
}
lector.close();
long fin = System.currentTimeMillis();
//Resultados
texto.append("Suma: " + sumaValores);
texto.append("\nTiempo: " + (fin - inicio) + " ms");
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(null, "No hay archivo de datos.", "Calculos de Hilo1",
JOptionPane.ERROR_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "Error accediendo a:\n" + archivo.getAbsolutePath(),
"Calculos de Hilo1", JOptionPane.ERROR_MESSAGE);
}
}
}
Y esta
Hilo2. Prácticamente son idénticas, solo cambian los datos que computan.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
/**
* De los años 2018, 2019, 2020 cual es el valor menor y mayor
*/
public class Hilo2 extends Thread{
private JProgressBar progreso;
private JTextArea texto;
public void setProgreso(JProgressBar progreso) {
this.progreso = progreso;
}
public void setTexto(JTextArea texto) {
this.texto = texto;
}
@Override
public void run() {
progreso.setValue(0);
texto.setText(" De los años 2018, 2019, 2020 calcular valor menor y mayor\n"
+ " ----------------------------------------------------------------------------------\n\n");
int menor = 5001;
int mayor = 0;
File archivo = new File("DatosVentas.txt");
try {
long inicio = System.currentTimeMillis();
BufferedReader lector = new BufferedReader(new FileReader(archivo));
String linea = lector.readLine();
while (linea != null) {
progreso.setValue(progreso.getValue() + 1);
String[] datos = linea.split(",");
int year = Integer.parseInt(datos[2]);
if (year >= 2018 && year <= 2020) {
int valor = Integer.parseInt(datos[3]);
if (valor > mayor)
mayor = valor;
if (valor < menor)
menor = valor;
}
linea = lector.readLine();
}
lector.close();
long fin = System.currentTimeMillis();
//Resultados
texto.append("Mayor: " + mayor);
texto.append("\nMenor: " + menor);
texto.append("\nTiempo: " + (fin - inicio) + " ms");
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(null, "No hay archivo de datos.", "Calculos de Hilo2",
JOptionPane.ERROR_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "Error accediendo a:\n" + archivo.getAbsolutePath(),
"Calculos de Hilo2", JOptionPane.ERROR_MESSAGE);
}
}
}
Bien, ya tendríamos los hilos.
Vamos a ver la interfaz del programa.
Este es el diseño que he creado:
Hay un botón para generar un archivo con los datos de ventas y otro botón para dar comienzo a los cálculos.
Al pulsar este último botón, se iniciarán los dos hilos y los resultados se verán en los dos paneles centrales.
Bien, el panel superior con el botón para crear el archivo, es un panel que he escrito como una clase separada porque tiene bastante líneas de código y además es completamente autónomo, no necesita interactuar con el resto de elementos de la interfaz, así que puede escribirse como una clase individual.
Esta clase usa un BufferedWriter dentro de un bucle para crear objetos de la clase
Venta y escribir una línea de texto con los datos en un archivo.
El programa pide crear 2 millones de registros (el archivo superará los 50MB de peso..
), esto puede requerir unos cuantos segundos según la potencia de computación de cada PC, así que antes de crear el archivo se muestra un mensaje advirtiendo de esto, para que el usuario sea consciente y sepa que ha de esperar.
Una vez creado el archivo, también se avisa al usuario para que sepa que el proceso ya se ha completado.
Esta es la clase
PanelCrearimport java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class PanelCrear extends JPanel{
private JButton btCrear;
public PanelCrear() {
btCrear = new JButton("Crear nuevo fichero de datos");
btCrear.addActionListener(new AccionCrear());
add(btCrear);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(25, 25, 25, 25),
BorderFactory.createLoweredSoftBevelBorder()));
}
private class AccionCrear implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "A continuación se creará un archivo con 2 millones de líneas."
+ "\nEsto puede tardar unos segundos según las capacidades de tu PC.\nTen paciencia...",
"Crear Archivo", JOptionPane.WARNING_MESSAGE);
File archivo = new File("DatosVentas.txt");
try {
BufferedWriter escritor = new BufferedWriter(new FileWriter(archivo, false));
//Creamos 2 millones de registros
for (int i = 1; i <= 2000000; i++) {
Venta nueva = new Venta(i); //Generamos nueva VENTA
escritor.write(nueva.toTXT()); //Creamos una línea de texto con los datos de la VENTA
escritor.newLine();
}
escritor.close();
JOptionPane.showMessageDialog(null, "Nuevos datos creados en:\n" + archivo.getAbsolutePath(),
"Crear Archivo", JOptionPane.INFORMATION_MESSAGE);
} catch (IOException e1) {
JOptionPane.showMessageDialog(null, "No se pudo crear archivo:\n" + archivo.getAbsolutePath(),
"Crear Archivo", JOptionPane.ERROR_MESSAGE);
}
}
}
}
Vamos a ver ahora los paneles que muestran el area de texto y la barra de progreso.
Se trata de una única clase, que mediante constructor recibe un entero entre 1 y 2 para indicarle si ha de trabajar con
Hilo1 o con
Hilo2.
Así no hay que escribir dos clases distintas, basta con escribir solo una.
En realidad es una clase con poco código.
Inicializa el area de texto y la barra de progreso.
Luego tiene un método llamado
calcular() que según el tipo de hilo que se le haya indicado, configura e inicia un
Hilo1 o un
Hilo2Así, con una sola clase, podemos disponer de dos paneles para cada tipo de hilo.
Esta es la clase
PanelHiloimport java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextArea;
import javax.swing.border.EtchedBorder;
public class PanelHilo extends JPanel{
private JProgressBar barraProgreso;
private JTextArea areaTexto;
int tipoHilo; //Se le indica al panel si ha de usar un Hilo1 o un Hilo2
public PanelHilo(int hilo) {
barraProgreso = new JProgressBar(0,2000000);
barraProgreso.setStringPainted(true);
areaTexto = new JTextArea(10, 33);
areaTexto.setEditable(false);
tipoHilo = hilo;
setLayout(new BorderLayout());
JPanel pnBarra = new JPanel();
pnBarra.add(barraProgreso);
pnBarra.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(15,15,15,15),
BorderFactory.createRaisedSoftBevelBorder()));
JPanel pnArea = new JPanel();
pnArea.add(areaTexto);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Hilo " + tipoHilo),
BorderFactory.createEtchedBorder(EtchedBorder.RAISED)));
add(pnBarra, BorderLayout.NORTH);
add(pnArea, BorderLayout.CENTER);
}
public void calcular() {
//Instanciamos un Hilo1 o un Hilo2 según el atributo tipoHilo
if (tipoHilo == 1) {
Hilo1 hiloCalculos = new Hilo1();
hiloCalculos.setProgreso(barraProgreso);
hiloCalculos.setTexto(areaTexto);
hiloCalculos.start();
}
else {
Hilo2 hiloCalculos = new Hilo2();
hiloCalculos.setProgreso(barraProgreso);
hiloCalculos.setTexto(areaTexto);
hiloCalculos.start();
}
}
}
Ya solo queda la clase principal. Es un JFrame que utiliza las clases JPanel que hemos visto antes y además añade el botón para iniciar los cálculos.
Al pulsar este botón se comprueba si existe o no un fichero de datos.
Si no existe, se avisa y no se hacen cómputos (porque no se puede).
Si existe, se le pide a los dos
PanelHilo mediante el método
calcular() que inicien sus correspondientes hilos.
Y enseguida podremos ver los resultados en pantalla.
Esta es la clase
Formularioimport java.awt.BorderLayout;
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.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Formulario extends JFrame{
private PanelHilo pnHilo1;
private PanelHilo pnHilo2;
private JButton btIniciar;
public Formulario() {
pnHilo1 = new PanelHilo(1); //Especificamos que "tipo" de hilo ha de usar
pnHilo2 = new PanelHilo(2);
JPanel pnHilos = new JPanel();
pnHilos.add(pnHilo1);
pnHilos.add(pnHilo2);
btIniciar = new JButton("Iniciar Calculos");
btIniciar.addActionListener(new AccionIniciar());
JPanel pnIniciar = new JPanel();
pnIniciar.add(btIniciar);
setLayout(new BorderLayout());
add(new PanelCrear(), BorderLayout.NORTH);
add(pnHilos, BorderLayout.CENTER);
add(pnIniciar, BorderLayout.SOUTH);
setTitle("Calculos con Hilos");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Formulario();
}
});
}
Y creo que con esto ya se tiene lo que se pide.
A ver si te sirve, y pregunta cualquier cosa que no entiendas.
Un saludo.