Anteriormente hemos visto:
Ejecución secuencial.
Uso de concurrencia heredando de la clase Thread e implementar la interfaz Runnable.
Particionamiento del trabajo entre los hilos.
Comparación del tiempo de la ejecución secuencial vs la ejecución paralela.
Ejecutores : Explicación, tipos, ventajas y desventajas.
Ejemplo de uso de ejecutores.
Entrelazado de instrucciones, sus consecuencias y código de ejemplo de entrelazado.
Solución e implementación de la solución al problema.
Explicación de synchronized, relación con los cerrojos y ejemplo de cerrojos.
Tipos de datos atómicos.
Control de acceso de hilos usando semáforos.
Control de sincronización a través del uso de barreras.
Ahora veremos:
Autómatas celulares 1D.
Tratamiento de imágenes(2D) con hilos.
Años atrás varios autores habían publicado estudios sobre distintas simulaciones de comportamiento de propagación de incendios, expansión de células tumorales, transferencia de calor y muchas más, todas estas simulaciones están basadas en modelos matemáticos y ayudan de manera significativa a ver como se desarrollan estas situaciones, estos modelos se llaman autómatas celulares, estas simulaciones se consiguen a través de la generación de nuevos valores que irán acorde a la expresión matemática que define el modelo, los autómatas celulares se componen de:
Con esto ya podemos definir de forma muy coloquial un autómata celular, voy a programar un autómata que vendrá definido de la siguiente manera:
Este sería el código:
public class Automata1D {
static int numCelulas = 10;// Obtener un conjunto inicial generado aleatoriamente.
public static int[] configuracionInicial() {
int configInicial[] = new int[numCelulas];for (int i = 0; i < numCelulas; i++) {
configInicial[i] = ((int) (Math.random() * numCelulas)) % 3;
}
return configInicial;
}
// A partir de una configuracionActual obtendremos una nueva.
public static int[] siguienteGeneracion(int[] configuracionActual) {
int nuevaConfiguracion[] = new int[numCelulas]; // Es necesario utilizar un array auxiliar.for (int i = 0; i < numCelulas; i++) {if (i == 0) { // En esta caso no podemos tomar la vecina izq. ya que nos salimos del array y// genera una excepcion. En este caso solo contaré con una vecina * 2.int vecinaDer = configuracionActual[i + 1];
nuevaConfiguracion[i] = (configuracionActual[i] + vecinaDer * 2) % 3; // Aplicamos la formula.
} else {
if (i == numCelulas - 1) { // Si estamos en la última posición del arrray, entonces no contaré con la// vecina derecha.int vecinaIzq = configuracionActual[i - 1];
nuevaConfiguracion[i] = (configuracionActual[i] + vecinaIzq * 2) % 3;
} else {
int vecinaIzq = configuracionActual[i - 1];int vecinaDer = configuracionActual[i + 1];
nuevaConfiguracion[i] = (configuracionActual[i] + vecinaIzq + vecinaDer) % 3;
}
}
}
return nuevaConfiguracion;
}
public static void main(String[] args) {
int inicial[] = configuracionInicial();System.out.println("La configuracion inicial(T) es:");
imprimirConfiguracion(inicial);int siguiente[] = siguienteGeneracion(inicial);System.out.println("La configuracion (T + 1) es:");
imprimirConfiguracion(siguiente);System.out.println("La configuracion (T + 2) es:");
imprimirConfiguracion(siguienteGeneracion(siguiente));
}
public static void imprimirConfiguracion(int[] configuracion) {
for (int i = 0; i < numCelulas; i++) {System.out.print(configuracion[i] + " ");
}
System.out.println("");
}
}
Y este sería el resultado de la ejecución:
Con esto ya tenemos un ejemplo de un autómata 1D, ahora veremos uno 2D usando el ejemplo de tratamiento de imágenes.
Para hablar de tratamiento de imágenes definiré unos conceptos de forma coloquial:
Existen diversos procesos que podemos realizar para modificar imágenes como la operación de suavizado, esta viene definida por la siguiente fórmula matemática:
Esta fórmula indica que el nuevo valor del PIXEL_i-j se calcula a partir de (4 * su valor actual + los valores de los píxeles vecinos) / 8 con esto obtenemos un píxel suavizado, en cierto modo es parecido a aplicar una media, veamos una definición de este autómata:
Os adjunto el código del suavizado procesado por una clase que hereda de Thread, cada hilo procesaran secciones distintas de la imagen(inicio, fin en el constructor de la clase):
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import javax.swing.JFrame;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import jdk.internal.org.objectweb.asm.tree.analysis.Frame;
public class Suavizado extends Thread {
static BufferedImage imagenInicial;
static BufferedImage imagenFinal;
static int dimension = 1000; // Número de filas y columnas de la imagen.int inicio, fin; // Con estos atributos definimos las secciones que tratara cada hilo.
public Suavizado(int i, int f) {
this.inicio = i;
this.fin = f;
}
public void run() {
// Declaramos las variables que contendran las posiciones a tomar, los valores y// el nuevo valor.int nuevoValor;int posicionIzq, posicionDer, posicionArriba, posicionAbajo;int vecinaIzq, vecinaDer, vecinaArriba, vecinaAbajo;for (int i = this.inicio; i < this.fin; i++) { // Con este for cada hilo ejecuta su seccionfor (int j = 0; j < dimension; j++) {// Calcular posiciones
posicionArriba = (j + 1 + dimension) % dimension;
posicionAbajo = (j - 1 + dimension) % dimension;
posicionIzq = (i - 1 + dimension) % dimension;
posicionDer = (i + 1 + dimension) % dimension;// Obtener valores de los vecinos
vecinaArriba = imagenInicial.getRGB(i, posicionArriba);
vecinaAbajo = imagenInicial.getRGB(i, posicionAbajo);
vecinaIzq = imagenInicial.getRGB(i, posicionIzq);
vecinaDer = imagenInicial.getRGB(i, posicionDer);// Aplicamos la funcion de transicion
nuevoValor = (4 * imagenInicial.getRGB(i, j) + vecinaAbajo + vecinaArriba + vecinaIzq + vecinaDer) / 8;// Asignamos el valor suavizado.
imagenFinal.setRGB(i, j, nuevoValor);
}
}
}
// Este método static crea una número de hilos en función al número de nucleos.
public static Suavizado[] lanzarHilos() {
int numeroNucleos = Runtime.getRuntime().availableProcessors();int filasPorHilos = dimension / numeroNucleos;
Suavizado[] hilos = new Suavizado[numeroNucleos];for (int i = 0; i < numeroNucleos; i++) { // Con este for creamos los hilos asignando las secciones.int inicio = filasPorHilos * i;int fin = filasPorHilos * (i + 1);
hilos[i] = new Suavizado(inicio, fin);
}
for (int i = 0; i < numeroNucleos; i++) { // Lanzamos los hilos
hilos[i].start();
}
return hilos; // Devolvemos los hilos tras haber sido lanzados.
}
// Este método static creará la imagen inicial de forma aleatoria.
public static BufferedImage inicial() {
// Creamos la imagen y usamos una variable del tipo Color de forma auxiliar.
BufferedImage imagen = new BufferedImage(dimension, dimension, BufferedImage.TYPE_BYTE_GRAY);
Colorcolor;for (int i = 0; i < dimension; i++) { // En este bucle asignamos valores aleatorios a la imagen.for (int j = 0; j < dimension; j++) {
float valorEscala = (float) Math.random();color = new Color(valorEscala, valorEscala, valorEscala);
imagen.setRGB(i, j, color.getRGB());
}
}
return imagen;
}
// En el main creamos la imagen, esperamos a los hilos y mostramos la imagen.
public static void main(String[] args) throws InterruptedException {
imagenInicial = inicial();
imagenFinal = new BufferedImage(dimension, dimension, BufferedImage.TYPE_BYTE_GRAY);
Suavizado[] hilos = lanzarHilos();for (int i = 0; i < hilos.length; i++) {
hilos[i].join();
}
mostrarImagenes();
}
// Este método static usa clases de Java swing para mostrar las imagenes.
public static void mostrarImagenes() {
JFrame frameInicial = new JFrame("Visualizador Inicial");
JFrame frameFinal = new JFrame("Visualizador Final");
JLabel imagen_inicial = new JLabel(new ImageIcon(imagenInicial));
JLabel imagen_final = new JLabel(new ImageIcon(imagenFinal));
frameInicial.getContentPane().add(imagen_inicial);
frameFinal.getContentPane().add(imagen_final);
frameInicial.pack();
frameFinal.pack();
frameInicial.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frameFinal.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frameInicial.setVisible(true);
frameFinal.setVisible(true);
}
}
Tras ejecutarlo se crearán dos ventanas(la primera sobre la segunda), obtenemos los siguientes resultados:
Espero que os sea de utilidad en alguna ocasión y si tenéis alguna cuestión o consejo para mejorar mis tutoriales por favor enviadme un mensaje, muchas gracias y un saludo 😃.