¿Como gestionar un número limitado de Threads?

Iniciado por imaginawireless, 20 Febrero 2014, 13:03 PM

0 Miembros y 1 Visitante están viendo este tema.

imaginawireless

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

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.

egyware

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.

adastra

 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

imaginawireless

Muchas gracias. Voy a estudiar lo que me decís.
Un saludo.

ThinkByYourself

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);
No te voy a engañar.
Todos hemos sido programados para normalizar la psicopatía de las élites económicas y políticas, y para realimentar su patrón de ciega codicia.

Pantera80

Hola. Yo hice un trabajillo que consistía en crear un pool fijo de dos hilos, aunque claro, se puede cambiar con una variable:


/**************************************************************************
     * 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();
    }
}

imaginawireless

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...

imaginawireless

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....

adastra

 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.

imaginawireless