Usar "static" en Concurrencia. ¿mala práctica?

Iniciado por fjlopezc17, 6 Mayo 2018, 16:11 PM

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

fjlopezc17

"Esta cuestión es un tema que se trata mucho en los foros. A mí no me gusta nada la declaración static, ya que implica por un lado que puedes usar métodos de un a clase sin necesidad de declarar objetos de la propia clase, o bien la compartición directa de atributos para todas las instancias de una clase. Ambos comportamientos, a mi entender son un mal uso de la programación orientada a objetos, pero son permitidos en JAVA (como muchas otras cosas)." Mi profesor.
¿Qué opináis sobre nuestro amigo "static"?

Independientemente de las discrepancias que cada uno pueda tener con esta declaración, me encuentro en el siguiente caso:
Si habéis tratado con Java Concurrente, seguramente sabréis que es muy usual utilizar el ejemplo del "Cocinero y Cliente"/"Productor y consumidor", etc. para ejemplificar el uso práctico de la concurrencia en Java.

El siguiente código que voy a mostraros ahora utiliza la variable entera static "tarta" y un 'lock' para el bloque synchronized también static. Creo que la pregunta es evidente, ¿si el uso de "static" es, supuestamente, y para algunos, una mala práctica, cómo podríamos resola declaración static?una forma alternativa a usar la declaración static?
/*

CONSUMIDOR:

- Si hay tarta = Me como una

- Si NO ... = Despertar COCINERO, dormir CONSUMIDOR.

COCINERO:

- Me duermo esperando a que un cliente me llame

- Si me llama produzco 10 trozos de tarta, me duermo.

*/

public class Principal implements Runnable {



     private boolean consumidor;

     private static int tarta=0;

     private static Object lock = new Object();

   

     public Principal(boolean consumidor) {

          this.consumidor=consumidor;

     }



     public void run() {

          while(true) {

                if(consumidor) {

                     consumir();

                }else {

                     cocinar();

                }

          }

     }

   

     private void consumir() {

          synchronized(lock) { //Cerrojo

                if(tarta>0) { //SI QUEDAN TARTAS...

                     tarta--; //Comer una tarta

                     System.out.println("Quedan "+tarta+" porciones de tarta.");

                     try {

                          Thread.sleep(1000);

                     } catch (InterruptedException e) {

                          e.printStackTrace();

                     }

                }else { //SI NO QUEDAN TARTAS...

                     lock.notifyAll(); //Despertar a el COCINERO que esperaba (abrir cerrojo, dejar pasar)

                     try {

                          lock.wait(); //Dormir al consumidor (hasta que lo despierten)

                     } catch (InterruptedException e) {

                          e.printStackTrace();

                     }

                }

          }

     }

   

     private void cocinar() {

          synchronized (lock) {

                if(tarta==0) {

                     tarta=10; //Cocinar 10 trozos de tarta

                     System.out.println("Soy el cocinero y quedan "+tarta+" trozos.");

                     lock.notifyAll(); //"¡Chacho! Que las tartas están listas!", Vale tío.

                } try {

                     lock.wait(); //dormir

                }catch(Exception e) {}

          }

     }

   

     public static void main(String[] args) {

          int numHilos = 11;

         

          Thread[] hilo = new Thread[numHilos];

         

          for(int i=0; i<hilo.length; i++) {

                Runnable runnable = null;

               

                if(i !=0) {

                     runnable = new Principal(true);

                }else {

                     runnable = new Principal(false);

                }

               

                hilo[i] = new Thread(runnable);

                hilo[i].start();

          }

         

          for(int i=0; i<hilo.length; i++) {

                try {

                     hilo[i].join();

                }catch(Exception e) {}

          }

     }

}


Serapis

#1
El uso de "Static", no es mala práctica, la mala práctica resulta de no entenderlo.

Un campo, función, objeto... en definitiva una entidad se debe declarar Static, cuando solo deba existir una única copia para cualquiera que sea el número de instancias que se creen. Y para que esto sea así, ha de requerirse que el campo, función u objeto no retengan... ni dependan de... ningún dato específico de una instancia.

Por ejemplo, al crea una instancia de un punto, cada punto puede tener valores distintos:
p1.X = 20
p1.Y = 12
en cambio un punto que sea el origen, puede declararse Static, porque siempre será.
p.X = 0
p.Y = 0
Dicho de otro modo, puede proveerse a la clase punto, un método estático, llamado Origen que lo que haga sea precisamente poner a 0 los campos 'X' e 'Y'... o proveer una instancia de un punto con valores 0,0 (para X e Y) y por lo mismo, no hay razón para que cada instancia deba proveer o implementar dicho método... es universal. Cumple el cometido que se le encomienda al modificador 'Static', una sola copia de ese método, vale para satisfacer todas las intancias.

La solución que preguntas pasa por declarar cuando proceda a determinados miembros como de solo lectura o solo escritura, así Tarta, parece que debe ser de solo lectura, no de escritura (desde fuera), o directamente un miembro privado y solo proveer trozos de tarta, y al caso a lo sumo proveer una propiedad "TrozosPorTarta", cuyo valor sea el que se tome internamente cuando deba crearse una nueva tarta.

Lo dicho para un campo, o propiedad, vale para cualquier otra entidad... un método Sort, por ejemplo, tomará un array (del tipo que sea) y lo ordenará, mientras opera con él usará variables internas para mantener el estado de operaciones, pero cuando termine, ese estado se pierde y una nueva llamada restablece el estado a su punto inicial, por ello un método Sort, es un candidato perfecto a ser declarado Static. Nada de una instancia específica queda retenuido en el método Sort, que altere una siguiente llamada al método.

En definitiva, Static, lo que hace es que exista una única copia, de lo que se declare Static, independientemente del número de instancias que existan, ahorrando con ello bastante memoria.

srWhiteSkull

Cita de: fjlopezc17 en  6 Mayo 2018, 16:11 PM
"Esta cuestión es un tema que se trata mucho en los foros. A mí no me gusta nada la declaración static, ya que implica por un lado que puedes usar métodos de un a clase sin necesidad de declarar objetos de la propia clase, o bien la compartición directa de atributos para todas las instancias de una clase. Ambos comportamientos, a mi entender son un mal uso de la programación orientada a objetos, pero son permitidos en JAVA (como muchas otras cosas)." Mi profesor.
¿Qué opináis sobre nuestro amigo "static"?

Mi consejo es que si buscas argumentos para rebatir con tú profesor es que no lo hagas(desiste), como estudiante dile que si y asiente con la cabeza, si lo que quieres es aprobar. La triste realidad es que el diseño de un lenguaje de programación no lo crean simples becarios, normalmente se trata de un equipo de señores muy experimentados con muchos títulines y masters del universo.

Los elementos estáticos en la programación no son un invento de Sun Microsystem, ya sobre los sesenta lenguajes como AGOL y el C habían implementado esto y lo veían como algo necesario. Y actualmente en múltiples lenguajes también han introducido la instrucción static (sin ir más lejos en el 2015 se aprobó el EMACS 6 que incluye también esto en la POO).

Tal como te subrayé en la cita es muy probable que sea una opinión personal.

Cita de: fjlopezc17 en  6 Mayo 2018, 16:11 PMEl siguiente código que voy a mostraros ahora utiliza la variable entera static "tarta" y un 'lock' para el bloque synchronized también static. Creo que la pregunta es evidente, ¿si el uso de "static" es, supuestamente, y para algunos, una mala práctica, cómo podríamos resola declaración static?una forma alternativa a usar la declaración static?

No, no es una mala práctica, peeero, lo ideal o recomendable es pasar los elementos que tienes definidos como estáticos, como un elementos compartido vía constructor o método. Ejemplo :


class Tarta extends Object {
   int tarta=0;
}

...

public class PruebaJava implements Runnable {  
   private boolean consumidor;
   private Tarta lockTarta ;
   
   public PruebaJava(Tarta lockTarta, boolean consumidor) {
       this.lockTarta     = lockTarta;
       this.consumidor    = consumidor;
   }

...

   public static void main(String[] args) {
       int numHilos = 2;
       Tarta tarta = new Tarta(); // elemento compartido
       Thread[] hilo = new Thread[numHilos];        

       for(int i=0; i<hilo.length; i++) {
           Runnable runnable = null;

           if(i !=0) {
               runnable = new PruebaJava(tarta, true); // todas las instancias comparten el mismo elemento
           }else {
               runnable = new PruebaJava(tarta,false); // todas las instancias comparten el mismo elemento
           }

...