Muy bien, esta es una clase de pregunta de doble propósito. Lo principal que espero sacar de esto es más conocimiento de multihilo. Soy un novato completo cuando se trata de multihilo, y este es mi primer intento real de hacer algo en múltiples hilos. La otra cosa que espero quitar es algo de ayuda con una tarea que estoy convirtiendo en un proyecto franken mucho más complicado para la diversión y el aprendizaje. En la primera parte de esta pregunta voy a detallar mi pensamiento y metodología para los hilos en los que he estado trabajando en mi tarea. Si estoy haciendo algo que es una mala práctica, necesita ser reparado, lo que sea, házmelo saber para que pueda aprender. De nuevo, no sé prácticamente nada sobre multihilo.Ayuda con multiprocesamiento en Java
En primer lugar, actualmente estoy tomando un curso de informática que, para ser agradable, tiene tareas que utilizan técnicas y estructuras de datos que ya he aprendido y que, por lo tanto, no representan un desafío. Para tratar de no aburrirme por completo, intento tomar un proyecto simple (crear una lista enlazada y una lista enlazada ordenada) y convertirlo en un programa franken multiproceso. Me gustaría que el método para agregar elementos nuevos esté en un hilo separado y obtenga entrada de una cola (no del todo necesario para una lista no ordenada, pero será más útil para el orden correcto), y para la lista ordenada quiero un hilo separado que patrullará la lista para asegurarse de que todos los elementos estén en orden (uno de los métodos que NO PUEDO cambiar devuelve referencias a nodos completos, que tienen datos completamente públicos). Esas son las dos cosas principales que me gustaría crear, pero definitivamente no son las únicas cosas que querría probar y descubrir. Esto pretende ser simplemente una prueba para aprender sobre el multihilo, por lo que no estoy especialmente diseñando este proyecto por su practicidad. Si desea dejar un comentario sobre lo que es una buena práctica de programación para lo que debe ser enhebrado o no, sería muy apreciado.
Como un paso que tomé hace poco, cuya idea fue obtenida al leer una publicación en StackOverflow, he creado una clase maestra que tiene Threads pasados para que pueda detenerlos y limpiar todo al final Del programa. Esto se hace mediante el uso de someThread.interrupt() y con el método run() del Thread para verificar esa excepción. Sin embargo, he encontrado un problema con este método: el bucle que se ejecuta a través de los hilos y llamando a la interrupción(), la mayoría de las veces, no se ejecuta. Aquí está el código para el método:
public static void stopAllThreads()
{
boolean stopped = false;
while(!stopped)
{
System.out.println("Stopping all threads");
synchronized(threads)
{
System.out.println("Threads being stopped: " + threads.size());
for(int i = 0; i < threads.size(); i++)
{
System.out.println("Stopping: " + threads.get(i));
threads.get(i).interrupt();
}
threads.clear();
stopped = true;
}
}
}
Al intentar depurar, lo puso en el bucle while para tratar de forzar el método a ejecutar finalmente el bucle, pero todo lo que sucede es que da salida a "Detener todo hilos "y luego nunca veo nada más. No sé si esta es una mala forma de codificar o qué está mal con este código. Se agradece cualquier ayuda para descubrir cómo hacer que esto funcione (si necesita ver más de la clase, hágamelo saber. Realmente no sé qué necesita ver, pero no quiero copiar y pegar varios archivos completos de Java).
Además de este problema, también he determinado que cuando ejecuto mi programa, llegará a la última línea e intentará detener todos los hilos antes de que el hilo pase por la cola de números para agregar a la lista. . Esto también es un problema cuando se intenta generar la lista a través de un método toString directamente después de agregar números a la cola, ya que no se agregará nada y, por lo tanto, no se generará nada en la consola. Aquí es mi método principal que muestra el orden de las cosas que suceden (aunque imagino que es menos importante cuando se trata de enhebrar):
public static void main(String[] args)
{
// always make sure this is the first thing created
ActiveThreads bla = new ActiveThreads();
Comparator<Integer> temp = new Comparator<Integer>()
{
public int compare(Integer i, Integer j)
{
if(i > j) return 1;
if(i < j) return -1;
return 0;
}
};
List<Integer> woogle = new List<Integer>(temp);
woogle.add(1);
woogle.add(2);
woogle.add(3);
woogle.add(4);
System.out.println(woogle);
ActiveThreads.stopAllThreads();
}
Por último, aquí es el hilo a medida que he hecho que se supone para comprobar la cola de elementos, a continuación, añadir todos los elementos en la cola a mi lista real:
private class AddThread implements Runnable
{
public AddThread()
{
}
public void run()
{
while(running)
{
synchronized(addQueue)
{
while(!addQueue.isEmpty())
{
System.out.println("Going through queue");
T temp = addQueue.poll();
if(head == null)
{
head = new Node<T>();
last = head;
head.data = temp;
largest = temp;
smallest = temp;
}
else
{
last.next = new Node<T>();
last.next.data = temp;
last = last.next;
if(mComparator.compare(temp, largest) == 1)
{
largest = temp;
}
else if(mComparator.compare(temp, smallest) == -1)
{
smallest = temp;
}
}
++size;
}
}
System.out.println("Pausing " + addThread);
synchronized(addThread)
{
pause(200);
}
}
}
private void pause(long time)
{
try
{
addThread.wait(time);
}
catch (InterruptedException e)
{
running = false;
addThread.notify();
}
}
private boolean running = true;
}
Ahora, el absoluto último que me gustaría pedir son algunos de los cebadores y las directrices básicas y como por una buena multihilo.He encontrado una buena cantidad de tutoriales, pero hay algunas cosas que las personas no mencionan en los tutoriales, y la mayoría de los tutoriales son regulares. Si conoces algunos buenos tutoriales, agradecería tener enlaces a ellos. Si hay algunas buenas metodologías de codificación para multihilo, eso sería muy apreciado también. Cualquier tipo de información que piense que podría ayudarme a enfocar mi cerebro en este concepto, y me aseguro de que al implementarlo lo haga de una forma menos propensa a los errores y la gente no me grite por lo malo. prácticas.
EDITAR: Para responder a las preguntas que matt b puso en un comentario, addThread es puramente una referencia al hilo que mira la cola que tiene elementos para agregar a la lista y los coloca en la lista. Es un hilo privado dentro de la clase que contiene la clase AddThread (uso inteligente de nombres, ¿eh?). Los hilos se construyen de una manera que se publicó en otra pregunta en StackOverflow. Aquí está un ejemplo (y también un fragmento de código que muestra lo que es addThread):
addThread = new Thread(new AddThread(), "Thread for adding elements");
addThread.setPriority(6);
addThread.start();
ActiveThreads.addThread(addThread);
sus temas se crean de una manera similar. En cuanto a lo que se supone que está haciendo el hilo que publiqué, lo expliqué en detalle anteriormente, pero trataré de poner una sinopsis aquí.
La clase AddThread es una clase interna que se crea en el momento de la construcción de la clase de lista vinculada que estoy tratando de hacer. Mira una cola privada que puede tener o no elementos, y cuando lo hace, coloca esos elementos en la lista vinculada. La cola también se agrega cuando se llama al método add (T newElt) de la lista vinculada.
EDITAR EDITAR: En aras de la divulgación completa, y para aquellos que deseen tomarse el tiempo, voy a publicar la totalidad de los dos archivos java relevantes aquí.
List.java
public class List<T> implements Iterable<T>, Comparable< List<T> >
{
// you may not change this inner class!
class Node<D>
{
D data;
Node<D> next;
}
public List(Comparator<T> comparator)
{
mComparator = comparator;
head = null;
last = null;
size = 0;
addQueue = new LinkedList<T>();
addThread = new Thread(new AddThread(), "Thread for adding elements");
addThread.setPriority(6);
addThread.start();
ActiveThreads.addThread(addThread);
}
public void add(T newElt)
{
synchronized(addQueue)
{
addQueue.add(newElt);
}
}
public T get(int index)
{
if(index > size)
throw new IndexOutOfBoundsException();
Node<T> temp = head;
for(int i = 0; i < index; i++)
{
temp = temp.next;
}
return temp.data;
}
public Node<T> lookup(T element)
{
Node<T> temp = head;
for(int i = 0; i < size; i++)
{
if(temp.data == element)
return temp;
temp = temp.next;
}
throw new NoSuchElementException();
}
public int size()
{
return size;
}
public boolean isEmpty()
{
return head == null;
}
public void delete(T element)
{
throw new UnsupportedOperationException("You must implement this method.");
}
public void replace(T oldElt, T newElt)
{
try
{
Node<T> temp = lookup(oldElt);
temp.data = newElt;
}
catch(NoSuchElementException e)
{
throw e;
}
}
public T getLargest()
{
return largest;
}
public T getSmallest()
{
return smallest;
}
public List<T> copy()
{
throw new UnsupportedOperationException("You must implement this method.");
}
public String toString()
{
StringBuffer ret = new StringBuffer();
int i = 0;
for(T x : this)
{
System.out.println("Loop: " + i++);
ret.append(x + " ");
}
return ret.toString();
}
public int compareTo(List<T> other)
{
throw new UnsupportedOperationException("You must implement this method.");
}
public ListIterator<T> iterator()
{
return new ListIterator<T>(head);
}
private class ListIterator<E> implements Iterator<E>
{
private ListIterator(Node<E> head)
{
cur = head;
}
public boolean hasNext()
{
if(cur == null)
return false;
System.out.println("Iterator: " + cur.data);
return cur.next == null;
}
@SuppressWarnings("unchecked")
public E next()
{
Node<E> temp = cur;
cur = cur.next;
return (E)temp.data;
}
public void remove()
{
throw new UnsupportedOperationException("You do NOT need to implement " +
"this method.");
}
private Node<E> cur;
}
private class AddThread implements Runnable
{
public AddThread()
{
}
public void run()
{
while(running)
{
synchronized(addQueue)
{
while(!addQueue.isEmpty())
{
System.out.println("Going through queue");
T temp = addQueue.poll();
if(head == null)
{
head = new Node<T>();
last = head;
head.data = temp;
largest = temp;
smallest = temp;
}
else
{
last.next = new Node<T>();
last.next.data = temp;
last = last.next;
if(mComparator.compare(temp, largest) == 1)
{
largest = temp;
}
else if(mComparator.compare(temp, smallest) == -1)
{
smallest = temp;
}
}
++size;
}
}
System.out.println("Pausing " + addThread);
synchronized(addThread)
{
pause(200);
}
}
}
private void pause(long time)
{
try
{
addThread.wait(time);
}
catch (InterruptedException e)
{
running = false;
addThread.notify();
}
}
private volatile boolean running = true;
}
private Comparator<T> mComparator;
private Node<T> head, last;
private Thread addThread;
// replace this with your own created class later
private Queue<T> addQueue;
private int size;
private T largest, smallest;
}
ActiveThreads.java
public class ActiveThreads
{
public ActiveThreads()
{
if(threads == null)
{
threads = new ArrayList<Thread>();
}
if(threadsMonitor == null)
{
ThreadsMonitor monitor = new ThreadsMonitor();
Thread thread = new Thread(monitor, "Active Threads Monitor");
thread.setPriority(3);
thread.start();
threadsMonitor = thread;
}
}
public static void stopMonitoring()
{
synchronized(threadsMonitor)
{
threadsMonitor.interrupt();
}
}
public static void addThread(Thread t)
{
synchronized(threads)
{
threads.add(t);
System.out.println("Added thread: " + t);
}
}
public static void addThread(Runnable r, String s)
{
Thread t = new Thread(r, s);
t.start();
addThread(t);
}
public static void stopThread(Thread t)
{
synchronized(threads)
{
for(int i = 0; i < threads.size(); i++)
{
if(threads.get(i) == t)
{
threads.get(i).interrupt();
threads.remove(i);
}
}
}
}
public static void stopAllThreads()
{
boolean stopped = false;
while(stopped == false)
{
System.out.println("Stopping all threads");
synchronized(threads)
{
System.out.println("Threads being stopped: " + threads.size());
for(int i = 0; i < threads.size(); i++)
{
System.out.println("Stopping: " + threads.get(i));
threads.get(i).interrupt();
}
threads.clear();
stopped = true;
}
}
}
private static ArrayList<Thread> threads = null;
private static Thread threadsMonitor = null;
private class ThreadsMonitor implements Runnable
{
public ThreadsMonitor()
{
}
public void run()
{
while(true)
{
synchronized(threads)
{
for(int i = 0; i < threads.size(); i++)
{
if(!threads.get(i).isAlive())
{
threads.get(i).interrupt();
threads.remove(i);
}
synchronized(threadsMonitor)
{
try
{
threadsMonitor.wait(5000);
}
catch(InterruptedException e)
{
threadsMonitor.interrupted();
return;
}
}
}
}
}
}
}
}
tooo longy! :) – rkg
Je, mis disculpas por eso. Realmente no sé lo que se necesita o no para publicarlo, y mis descripciones terminaron por ser muy largas, desafortunadamente. – Freezerburn
¡Debes aplaudir su esfuerzo! –