Foro de elhacker.net

Programación => Java => Mensaje iniciado por: imaginawireless en 20 Febrero 2014, 13:03 pm



Título: ¿Como gestionar un número limitado de Threads?
Publicado por: imaginawireless en 20 Febrero 2014, 13:03 pm
Hola gente.

Estoy jugando con el protocolo SNMP utilizando SNMP4J. Por ejemplo a la hora de crear un receptor de Traps, me llama la atención que ya viene preparado para definir cuantos hilos queremos ejecutar simultaneos cuando dicho servicio una vez iniciado , mediante la clase 'TheadPool'.

http://www.snmp4j.org/doc/org/snmp4j/util/ThreadPool.html (http://www.snmp4j.org/doc/org/snmp4j/util/ThreadPool.html)

He intentado realizar lo mismo para otros servicios que he desarrollado siguiendo el código de dicha clase y demás, pero se me está complicando.
¿A alguien se le ocurre un forma de definir de una forma sencilla cuantos hilos puede por ejemplo ejecutar un ServerSocket como máximo?

Muchas gracias.


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: egyware en 20 Febrero 2014, 13:08 pm
Tal vez tengas que mirar con más atención al Patrón Pool

http://programacionsolida.com.ar/2012/02/patrones-de-diseno-de-creacion-object.html

Un link que encontré a la rápida te puede guiar un poco.


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: adastra en 20 Febrero 2014, 13:20 pm
 En java tienes el "Executor Framework" que te permite gestionar paralelamente, varios grupos de hilos. Se basa en un modelo que utiliza colas y bloqueos, el cual probablemente es uno de los patrones más utilizados en casi todos los lenguajes de programación modernos en lo que a concurrencia se refiere.
Dale un vistazo al paquete java.util.concurrent y busca tutoriales sobre su uso.
Uno muy bueno, puede ser este:


http://www.vogella.com/tutorials/JavaConcurrency/article.html (http://www.vogella.com/tutorials/JavaConcurrency/article.html)


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: imaginawireless en 20 Febrero 2014, 14:05 pm
Muchas gracias. Voy a estudiar lo que me decís.
Un saludo.


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: ThinkByYourself en 21 Febrero 2014, 10:09 am
No sé si es lo que buscas, pero a ServerSocket le puedes delimitar cuántas peticiones máximas quieres que maneje en el constructor con un entero:
new ServerSocket(int puerto, int peticionesSimultaneasMaximas);


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: Pantera80 en 22 Febrero 2014, 08:44 am
Hola. Yo hice un trabajillo que consistía en crear un pool fijo de dos hilos, aunque claro, se puede cambiar con una variable:

Código:
/**************************************************************************
     * ejecuta ocho veces la tarea NumerosAleatorios que imprime diez números
     * aleatorios menores que cincuenta, mediante un pool de tan sólo dos hilos
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
       
        //define un pool fijo de dos hilos
        ExecutorService executor = Executors.newFixedThreadPool(2);

        //pasa 30 tareas NumerosAleatorios al pool de 2 hilos
        for (int i = 1; i <= 30; i++) {
            executor.submit(new NumerosAleatorios());
        }

        //ordena la destrucción de los dos hilos del pool cuando hayan
        //completado todas las tareas
        executor.shutdown();
    }
}


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: imaginawireless en 23 Febrero 2014, 12:02 pm
Gracias por vuestras respuestas.

La idea es optimizar el uso de recursos en un servicio que está a la escucha de conexiones, sobre todo cuando lleva mucho tiempo activo.

He leído lo que me ponían 'adastra' y 'egyware'. Con ello he encontrado nuevo mundo, jejeje, los patrones de diseño. Estoy estudiando distintos patrones para ver cual se adapta mejor a lo que busco.

En cuanto lo tenga funcionando os lo pongo.

Mil gracias...


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: imaginawireless en 23 Febrero 2014, 19:17 pm
Hola gente. Después de leerme e intentar comprender el patrón "Object Pool", este es el resultado.

El servicio que escucha "Listener" y ejecuta el parseo de datos en un nuevo hilo llamando a "Parser" desde el pool.
Citar
public class Listener implements Runnable {

    private boolean serviceRun;
    private DatagramSocket socket;
    private SysLogParser parser;
    private DatagramPacket data;
    private Thread runThread;
    private int bufferSize;

    private void init() {
        bufferSize = 2048;
        setServiceRun(true);
    }

    @Override
    public void run() {
        init();
        try {
            System.out.println("Servicio iniciado\n");
            data = new DatagramPacket(new byte[bufferSize], bufferSize);
            socket = new DatagramSocket(10100);
            while (isServiceRun()) {
                try {
                    socket.receive(data);
                    parser = ListenerPool.getInstance().checkOut(data.getData());
                    parser.start();
                } catch (IOException e) {
                    System.out.println("Error Listener: " + e.getMessage());
                }
            }
        } catch (Exception e) {
            System.out.println("Error Listener2: " + e.getMessage());
        }
    }

    public boolean isServiceRun() {
        return serviceRun;
    }

    private void setServiceRun(boolean serviceRun) {
        this.serviceRun = serviceRun;
    }
   
    public void start(){
        if (runThread == null){
            runThread = new Thread(this);
            runThread.start();

        }
    }
   
    public void stop(){
        if (runThread != null){
            setServiceRun(false);
        }
    }
}

La clase que gestiona el pool "ListenerPool" para la clase "Parser"
Citar
public class ListenerPool {

    private static ListenerPool instance = new ListenerPool();
    private long expirationTime;
    private HashMap<Parser, Long> locked, unlocked;

    private ListenerPool() {
        expirationTime = 5000;
        locked = new HashMap<Parser, Long>();
        unlocked = new HashMap<Parser, Long>();
    }

    public static ListenerPool getInstance() {
        createInstance();
        return instance;
    }

    private static void createInstance() {
        if (instance == null) {
            synchronized(ListenerPool.class) {
                if (instance == null) {
                    instance = new ListenerPool();
                }
            }
        }
    }
   
    protected Parser create(byte[] data) {
        return new Parser(data);
    }

    public boolean validate(Parser o) {
        return true;
    }
   
    public synchronized Parser checkOut(byte[] data) {
        long now = System.currentTimeMillis();
        Parser t;
        if (unlocked.size() > 0) {
            Iterator it = unlocked.keySet().iterator();
            while (it.hasNext()) {
                t = (Parser) it.next();
                if ((now - unlocked.get(t)) > expirationTime) {
                    unlocked.remove(t);
                    expire(t);
                    t = null;
                } else {
                    if (validate(t)) {
                        unlocked.remove(t);
                        locked.put(t, now);
                        return (t);
                    } else {
                        unlocked.remove(t);
                        expire(t);
                        t = null;
                    }
                }
            }
        }
        t = create(data);
        locked.put(t, now);
        return (t);
    }

    public synchronized void checkIn(Parser t) {
        locked.remove(t);
        unlocked.put(t, System.currentTimeMillis());
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
       throw new CloneNotSupportedException();
    }
}

El parseador de datos "Parser"
Citar
public class Parser implements Runnable {

    private byte[] data;
    private Thread runThread;

    public Parser(byte[] data) {
        this.data = data;
    }

    private void show(){
        System.out.println(runThread.getId() + "@" + runThread.getName());
    }

    @Override
    public void run() {
        this.showLog();
        SysLogParserPool.getInstance().checkIn(this);
    }
   
    public void start(){
        if (runThread == null){
            runThread = new Thread(this);
            runThread.start();
        }
    }
}

Si os fijáis en la salida que nos da el método show() de "Parser". ¿No debería de manear hilos con el mismo nombre? Se están creando nuevos hilos de la clase Parser, cuando deberían tener el mismo nombre, ¿no?


Salida
Citar
11@Thread-1
12@Thread-2
13@Thread-3
14@Thread-4
15@Thread-5
16@Thread-6
17@Thread-7
18@Thread-8
etc....


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: adastra en 24 Febrero 2014, 10:01 am
 Por lo que estoy viendo, estas creando un hilo de ejecución para crear una instancia del Pool, después creas más instancias "Parser" que a su vez también son hilos...
Lo normal que la clase padre (Pool) maneje múltiples hijos de ejecución desde un único hilo, es decir, que la clase Listener no debería ser Runnable, simplemente debería instanciar la clase de Pool y en dicha clase, insertar los 1 o n hilos de la clase Parser. Luego, según el patrón de "Executor", utilizando una instancia del Pool, debes arrancar (o detener) el grupo de hilos que estén asociados a dicho Pool...


Lo suyo seria algo como:

1. Listener instancia Pool
2. Desde un método de factoría de Listener (getInstance por ejemplo) crear N hilos (instancias de la clase Parser) y añadirlos a una estructura interna de la clase Pool (una Lista de objetos Parser)
3. Desde un método de la clase Listener (startThreads, iniciarHijos o como quieras llamarlo) iniciar todos los hilos asociados a la instancia Pool creada inicialmente.


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: imaginawireless en 24 Febrero 2014, 13:19 pm
Ok. Voy a revisarlo. Gracias por responder.


Título: Re: ¿Como gestionar un número limitado de Threads?
Publicado por: imaginawireless en 23 Marzo 2014, 12:38 pm
Hola chicos. Perdonar por el retraso al contestar.
He estado probando con distintos patrones de diseño y el resultado final es que depende del entorno de trabajo o necesidades, no son muy viables.

En el ejemplo que nos ocupa, tengo un servidor de Syslog. El caso es que cuando al servidor le llegan pocos eventos de otros hosts, no hay problemas y procesa todos los mensajes, pero cuando el número de mensajes recibidos aumenta, no es capaz de recoger todos haciendo uso del patrón Pool.

Creo que se vuelve muy pesado el chequeo del Pool, y los paquetes UDP llegan al máximo de tiempo dando un TIMEOUT en el Listener.

No se que opináis, pero personalmente no veo opción mediante patrones para reutilización de objetos en este caso.