style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

forma correcta singleton

como seria la forma correcta de implementar un singleton, cuando digo forma correcta es teniendo en cuenta todo el tema de hilos y concurrencia.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de Sr. Negativo

Forma correcta del Singleton

Aquí hay un ejemplo de crear un Singleton (inglés)

La solucion Bill Pugh saca

La solucion Bill Pugh saca provecho de la forma en la que se cargan las clases en Java

http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh

Y tiene la ventaja que es threadsafe y crea el singleton hasta que se requiere ( de forma 'lazy' )

Antes de que lo implementes asegurate que lo necesitas, aunque hay usos legitimos del Singleton puede ser que tambien lo estes queriendo usar en forma de antipatron.

Revisa esta respuesta: http://stackoverflow.com/a/138012/20654

recomiends esto

esta es una palntilla que recomiendan
no se con esta plantilla tenga problemas con mi listener en java (spluciones multihilos)

public class DemoSingleton implements Serializable {
        private static final long serialVersionUID = 1L;

        private DemoSingleton() {
                // private constructor
        }

        private static class DemoSingletonHolder {
                public static final DemoSingleton INSTANCE = new DemoSingleton();
        }

        public static DemoSingleton getInstance() {
                return DemoSingletonHolder.INSTANCE;
        }

        protected Object readResolve() {
                return getInstance();
        }
}

estoy extendiendo un runnable de un singleton

Mi pregunta es saber si con este singleton asi como esta no tendria problemas en la web. me refiero a problemas de concurrencia que la trama de informacion no se me vaya alterar de una u otra forma.
ya que el aplicativo va a correr con un java web start.

public class ClienteServiceGenerico implements Serializable,Runnable
{
        private static final long serialVersionUID = 1L;
        private static Socket socket;
        private static PrintStream out;
        private static BufferedReader bufferInput;
         
        private ClienteServiceGenerico()
        {
           
                // private constructor
        }
       
        public void conectar(String path)
        {
           try
           {
               ClienteServiceGenerico.socket=ClienteServiceGenerico.conect(path);
           }
           
           catch(UnknownHostException e)
          {
             System.out.println("error"+e.getMessage());
          }
   
          catch(IOException e)
         {
                UtilFrame.setMensajeError("No es posible realizar Conexion con El Servidor. \n Intente mas tarde", "MENSAJE ERROR");
                System.exit(0);
   }
        }
       
        public boolean send(String message)
       {
           boolean ok ;
           try {  
           
            out.println(message);
            out.flush();
           
            ok=true;
         
        } catch (Exception ex) {
            if(out!=null)
            {
               out.close();
            }
            ok=false;
           
        }
       
        return ok;
   
    }
        private static Socket conect(String path) throws SocketException, UnknownHostException, IOException
        {
          try
          {
               String ip [] = UtilRed.validarIpSocket();
               ConfiSys config = UtilCarga.cargaProperties(path);
               ClienteServiceGenerico.socket = new Socket(ip[0],6667);
               ClienteServiceGenerico.out = new PrintStream(socket.getOutputStream());
          }
          catch(SocketException ex)
          {
               throw new SocketException(ex.getMessage());
          }
       
          catch (UnknownHostException ex)
          {
               throw new UnknownHostException(ex.getMessage());
          }
          catch (IOException ex)
          {
               throw new IOException(ex.getMessage());
          }
          return socket;
        }
   
    @Override
    public void run()
    {
        String tramaRespuestaServer;
         try {
              ClienteServiceGenerico.bufferInput = new BufferedReader(new InputStreamReader(socket.getInputStream()));
              while((tramaRespuestaServer = (String) bufferInput.readLine())!=null)
              {
                   
                 
               
               }
             
             
         } catch (IOException ex) {
           
             Logger.getLogger(ClienteServiceGenerico.class.getName()).log(Level.SEVERE, null, ex);
         }
       
    }
     
        private static class DemoSingletonHolder
        {
                public static final ClienteServiceGenerico INSTANCE = new ClienteServiceGenerico();
        }
        public static ClienteServiceGenerico getInstance()
        {
                return DemoSingletonHolder.INSTANCE;
        }
        protected Object readResolve()
        {
                return getInstance();
        }
   
}

Primer comentario, regresa al

Primer comentario, regresa al estilo de Java de poner la llave que abre en la misma linea.

public void do() {
}
public void doNot()
{
}

Aunque parece menor, seguir la convención permite leer mas fácilmente el código.

La idea del singleton es que no tengas que usar variables de clase, entonces no hay razón para tener socket, buffer input y out como tales. Deberían de ser variables de instancia del singleton ( que a su vez es una variable de clase en si mismo ).

Una aplicacion webstart es una aplicación cliente y como tal no tendrías problemas pues solo habría un hilo corriendo, a menos que en tu aplicación crearas multiples hilos, entonces si tendrias problemas porque vas a hacer que dos hilos escriban al mismo lugar ( la variable out ). De hecho al hacerlo singleton y/o variable de clase garantizas tener problemas ( no tendrías problemas si fueran variables de una función ) Y tendrías que sincronizar el acceso a la variable compartida lo cual complica el código y es mas propenso a errores.

Antes de seguir sospecho que quieres optimizar una solución algo que quiza puede hacerse de otra forma asi que la pregunta es: porque quieres usar un singleton? Que beneficio buscas al usar un singleton?

Un singleton ayuda a simplificar la programación al permitir al acceso a un objeto que por su naturaleza en la aplicación es único, por ejemplo, el sistema de archivos.

Porque no abres una conexiona cada vez que quieras mandar un mensaje y/o por cada hilo que tengas? Asi no tendrías ningún problema de corrupción de datos porque cada cliente va a tener solo un canal para comunicarse.

De nuevo, quizá haya mejores formas, pero antes habría que saber que es lo que quieres hacer.

Saludos.

ok

es un programa cliente que se va a comunicar con mi servidor(Listener en java) que maneja un pool de conexiones, por ahora el server trabaja de los mas de bien. la idea del singlenton es tener como todo a mano en cualquier lugar osea la idea es hacer algo que no solo me ayuda a mi sino a los otros desarrolladores que quieran hacer un programa que se comunique con el listener en java; con el singleton buscaba hacer la conexion, enviar la informacion al server y recibir dicha informacion del server (y tener dicha respuesta disponible globamente en la clase que la necesite usar.) y finalmente cerrar conexion. para cualquier desarrollador que necesite enviar info al server java y recibir una respuesta lo usara atraves de esa clase singlenton.

si hay una alternativa mucho mas profesional y mas optima de como hacerlo seria genial que me lo indicaras.
saludos desde colombia.

Aja, pero y porque como

Aja, pero y por que como singleton y no como una clase normal?

import your.magic.Client;
class Application {

   public void someMethod() {
        Client hackChanClient = new Client(  Constants.SOME_SERVER_ADDRESS );
        hackChanClient.send( "shutdown" );  
    }
}

Que es lo que buscas del singleton?

singlenton

que fuera una unica instancia y que al hacer la peticion el cliente la respuesta que me envia el server la tengo ahi siempre disponible, como si fuera una variable global.

actualmente lo que hago es que desde todas las aplicaciones cliente desde el controler le agrego una clase interna
ListenerSocket la idea es hacer una clase generica que todas mis aplicaciones clientes la usen para enviar y recivir la info del servidor
y no que cada aplicativo en su controler le añada la clase ListenerSocket. la idea con el singleton aparte de tener una sola instancia era que el se encargara de enviar y recivir los dastos del server y yo lo usura en cualquier lado como si de una variable global se tratase.

public class controladorAplicativocliente1
{
    private Socket socket;
    privateMessage message;
    private ClienteService service;

    private  class ListenerSocket implements Runnable{
       
        private ObjectInputStream input;

        public ListenerSocket(Socket socket)
        {
            try {
                this.input = new ObjectInputStream(socket.getInputStream());
            }
            catch (IOException ex) {
                Logger.getLogger(ClienteFrame.class.getName()).log(Level.SEVERE, null, ex);
            }
           
        }
        @Override
        public void run()
        {
            try {
                while((message=(Message)input.readObject())!= null)
                {
                    Action action = message.getAction();
                    if(action.equals(Action.CONNECT))
                    {
                        connect(message);
                   
                    }  }
            } catch (IOException ex) {
                Logger.getLogger(ClienteFrame.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(ClienteFrame.class.getName()).log(Level.SEVERE, null, ex);
            }
   
        }
   
         
   
    }

 private  void connect(tMessage message){
       
       this.txtAreaReceive.append(message.getName() + "\n");
    }

private void Send(){

         String name = txtName.getText();
       
        if(!name.isEmpty())
        {
            this.message = new Message();
            this.message.setAction(Action.CONNECT);
            this.message.setName(name);
            if(this.socket == null)
            {
                this.service = new  ClienteService();
                this.socket =this.service.connect();
                new Thread(new ListenerSocket(this.socket)).start();
           }
           
            this.service.send(message);
        }
    }                            

}
   
}

Son dos partes separadas.

Son dos partes separadas. Hacer una clase y distribuirla en una bibliioteca esta genial, no solo no tienen que escribir desde el inicio la logica, sino que ademas se benefician de las mejoras que se hagan.

Que esa clase tenga que ser un singleton es otra cosa. Puede ser un singleton en el sentido en el que solo hay un canal de comunicacion y se obtiene desde un solo punto:

ClientService client = ClientService.getInstance();
client.send("mensaje");

Pero aun pueda mandar varios mensajes a la vez. En la implementacion que tienes al usar una variable de clase para "out" tendrias el problema que si tu aplicacion manda dos mensajes a la vez, el mensaje llegara mezclado. Para evitarlo puedes hacer que tu singleton cree una conexion cada vez o encole los mensajes. En servidor tiene un solo socket server, pero puede tener varios sockets clientes, no deberia de haber razon por la cual tu cliente tuviera solamente una conexion.

ok

descartemos el singlenton creo que lo pende erróneamente al verlo como un acceso global.
la idea de todo es que el los aplicativos clientes tenga una especie de clase re utilizable para cualquier aplicativo java que le maneje el envió y recepción de los datos. la idea es evitar en cada programa cliente el ejemplo que te mostré anteriormente como lo trabajo con una clase anidada.

eso que comentas que se mezclan datos seria garrafal, ya que son como 1500 usuarios conectandose al aplicativo cliente.
por lo genereal lo que hago siempre en conectarme enviar datos al sever , el server me responde y termino conexion tomo esos datos que me llegaron del server los procesos y envio otra vez peticion al server en una nueva conexion recivo respuesta del server y cierro
por lo general la idea es que el server responda y se libere.

server timeout

actualmente mi servidor maneja un timeout de 40 seg, la idea es bajarlo a 7 o 5 seg ya que manejo todo por demanada cliente se conecte envia a alserver server responde y se termina la conexion con esa info del server el cliente hace lo que tiene que hacer y envia nueva peticion al server.

El singleton es por JVM, si

El singleton es por JVM, si tienes 10 programas corriendo en 10 JVMs diferentes tienes un singleton distinto en cada programa. Lo mismo si tienes 1,500 usuarios con una aplicacion, hay 1,500 "singletons" diferentes. El concepto aplica solo a nivel programa dentro de una JVM.

La idea del singleton no esta mal, pero en su implementacion puedes usar internamente varias conexiones o crear distintos mensajes, ni tiene porque usar uno solo el mismo. De esa forma sigues teniendo un solo punto de acceso para enviar mensajes y nadie tiene que reinvertar la solucion cada vez.

Imagen de ezamudio

serializable?

Digo ya leí lo demás y veo que has llegado a la conclusión de desechar el singleton; bien. Pero es que leí el primer comentario y lo primero que me saltó a la vista fue "qué? un singleton Serializable?" mmmm un singleton no puede ser Serializable, y un objeto que implementa Serializable no puede ser un singleton.

En caso que las razones no sean obvias: suponte que tienes tu Singleton, sólo existe una instancia de tu clase en el server. Pero al serializarlo, estás enviando una copia a otro lugar; al llegar a su destino cuando lo deserialicen tendrán una instancia nueva de una clase que se supone que debe ser un singleton. No importa que el constructor sea privado, esto lo puedes hacer desde otra clase y se crean nuevas instancias. Por ejemplo:

  final ClienteServiceGenerico s1 = ClienteServiceGenerico.getInstance();
  //Escribimos el singleton a un stream
  ByteArrayOutputStream bout = new ByteArrayOutputStream();
  ObjectOutputStream out = new ObjectOutputStream(bout);
  out.writeObject(s1);
  out.flush();
  //Creamos otro stream desde donde vamos a leer el singleton serializado
  ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
  ObjectInputStream oin = new ObjectInputStream(bin);
  final ClienteServiceGenerico s2 = (ClienteServiceGenerico)oin.readObject();
  System.out.println("s1=" + s1 + "; s2=" + s2 + " iguales?" + (s1==s2));
  //imprime dos strings distintos (por aquello de los hashes) y dice que no son iguales.

ezamudio..

la idea no era enviar el singleton como tal

out.writeObject(s1);

actualmente evio y recibo trama es un String de tamaño variable con una parte fija (encabezado) y una parte de datos que puede ser variable
las trama que manejo tienen las siguientes estructura ejemplo 005412500000078456987PC0000007411| dato1,dato2,dato3|
los primeros 4 bytes tamaño de la trama 0054
los siguientes 3 bytes tamaño es el codigo de transaccion 125 (segun este codigo ejecuto un paquete en oracle)
los siguientes dos bytes 00 es el estado de la transaccion 00 todo ok
los siguientes 12 bytes 000078456987 id de usuario
los siguientes 12 bytes PC0000007411 maquina de usuario

lo que esta en | dato1,dato2,dato3| son los datos variables

cuando envio una de esta tramas al server y el server me responde el encabezado debe ser el mismo lo unico que vario es la parde de datos
hago un minimo de comprobaciones que el tamaño de la trama coincida con los primeros 4 bytes y que dicha trama me pertenece juntando
la parte idusuario y host si la trama que recibo tiene mi id junto con mi host la trama es mia sino la desecho esta comprobacion la empeze hacer por que una vez me paso que algunas ocaciones las tramas se me mezclaba en no se que punto y me llegaban trama que no le pertenecian a otro usuario no supe si el error era del listener o de la base de datos del programa cliente (JAVA WEB START aprox 1500 usuarios concurrentes).

el singlenton era para manejar la conexion y el evio de datos al server y el input y output me los manejara el singleton

public class SendDTO {

    private Socket socketTransaction = null;
    private InetSocketAddress socketAdress = null;
    private InputStream socketEntrada= null;
    private PrintStream socketSalida = null;

    private static TransmisionDTO instance = null;

   

    public static TransmisionDTO getInstance() {
        if(instance == null){
                        try {
                                instance = new TransmisionDTO();

                        } catch (Exception e) {
                                e.printStackTrace();
                        }
                }
                return instance;
    }

    public Socket getSocketTransaction() {
        return socketTransaction;
    }

 
    public void setSocketTransaction(Socket socketTransaction) {
        this.socketTransaction = socketTransaction;
    }

    public InetSocketAddress getSocketAdress() {
        return socketAdress;
    }

 
    public void setSocketAdress(InetSocketAddress socketAdress) {
        this.socketAdress = socketAdress;
    }

 
    public InputStream getSocketEntrada() {
        return socketEntrada;
    }

    public void setSocketEntrada(InputStream socketEntrada) {
        this.socketEntrada = socketEntrada;
    }

 
    public PrintStream getSocketSalida() {
        return socketSalida;
    }

 
    public void setSocketSalida(PrintStream socketSalida) {
        this.socketSalida = socketSalida;
    }

}

y en otra clase hacaer algo como

try{

        SendDTO  tr = SendDTO .getInstance();
        socketTransaction = tr.getSocketTransaction();
        sIn = tr.getSocketEntrada();
        sOut = tr.getSocketSalida();

        if(socketTransaction == null ){
            System.out.println("crea conexion");
            socketTransaction = new Socket (IP, puerto );
            tr.setSocketTransaction(socketTransaction);
        }
        socketTransaction.setSoTimeout(readSocketTimeout);
        if(sIn == null){
            sIn = tr.getSocketTransaction().getInputStream();
            tr.setSocketEntrada(sIn);
        }

        if(sOut == null ){
            sOut = new PrintStream(tr.getSocketTransaction().getOutputStream());
            tr.setSocketSalida(sOut);
        }

        //envia el mensaje
        sOut.write(mensajeSalida);
        sOut.flush();
        lenInput = 0;
        long time = System.currentTimeMillis() + readSocketTimeout;
        while(System.currentTimeMillis() < time){
            lenInput = sIn.available();

            if(lenInput > 0){
                byte [] textoBytes = new byte [lenInput];
                for(i=0; i<lenInput; i++){
                    textoBytes[i]= (byte)sIn.read();
                    texto= texto + (char)textoBytes[i];
              }
                break;
            }
    }
        if(lenInput == 0){
            socketTcpClose();
            throw new IOException (" Error recibiendo datos ");
        }

y mi singleton sendDTo tiene tanto mi input como mi ouput y los tengo como de una manera global que puedo usar donde lo necesite
algo asi era mi idea y para lo que realmente queria usar el singleton.

Imagen de echan

hey hackchan usar variables

hey hackchan usar variables globales en un entorno concurrente es la peor idea que puedes tener, nada impide que mas de un hilo modifique el estado compartido y empieces a ver inconsistencias.

Podrias para controlar el acceso usando bloqueo.

http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

Otra manera mediante estructuras concurrentes como un ConcurrentMap o una BlockingQueue.

Imagen de Cid

Perdón por abrir de nuevo

Perdón por abrir de nuevo esta discusión, pero tengo una duda mencionan en este y un post más reciente acerca del patron Singletón y los objetos Serializables son mutuamente excluyentes porque la serialización es contranatura al patrón que sirve para genera una instancia única y no más de un objeto, pero bueno he visto que este vínculo si realiza un singleton con el siguiente fragmento de código:

package com.journaldev.singleton;
 
import java.io.Serializable;
 
public class SerializedSingleton implements Serializable{
 
    private static final long serialVersionUID = -7604766932017737115L;
 
    private SerializedSingleton(){}
     
    private static class SingletonHelper{
        private static final SerializedSingleton instance = new SerializedSingleton();
    }
     
    public static SerializedSingleton getInstance(){
        return SingletonHelper.instance;
    }
     
}

Dice ahí que si agrego este método devolverá el mismo hashCode pero lo que estoy invocando es un atributo de Clase y jamás se serializó verdad ?, mmm pero me surge una pregunta ¿ Un atributo de clase se carga cada vez que se carga la clase, entonces si lo cargo en diferentes JVM sigue siendo singleton ? lo que leí es que readResolve es utilizado en lugar del metodo readObject de la clase InputStream que debó de sobre escribir para que lo utilice en lugar de readObject y así lea la misma intancia en lugar de que generé una nueva.

protected Object readResolve() {
    return getInstance();
}

Al final entonces no se serializa nada o sí ?

Imagen de ezamudio

jvms

Obvio tiene que ser un objeto distinto en distintas jvms. Son distintos procesos, no te va a dar un proxy automágicamente al objeto de la primera jvm donde se encuentra.

La idea del singleton es que solamente exista UNA instancia en toda la jvm que está corriendo.

Si sobreescribes readResolve entonces efectivamente utilizas la instancia existente del singleton en vez de deserializar la que viene del stream. Sí se serializa el objeto, pero no se usa.

Por qué no ponemos fin de una vez a todo esto de los singletons serializables, no con discusiones, sino con PRUEBAS?

class PruebaSingleton1 implements Serializable {
  private static final long serialVersionUID=1
  public static final PruebaSingleton1 instance = new PruebaSingleton1()

  private PruebaSingleton1(){}
}

class PruebaSingleton2 implements Serializable {
  private static final long serialVersionUID=1
  public static final PruebaSingleton2 instance = new PruebaSingleton2()

  private PruebaSingleton2(){}

  protected Object readResolve() {
    return PruebaSingleton2.instance;
  }
}

final ByteArrayOutputStream bout = new ByteArrayOutputStream()
final ObjectOutputStream out = new ObjectOutputStream(bout)
out.writeObject(PruebaSingleton1.instance)
out.writeObject(PruebaSingleton2.instance)
out.close()
final ObjectInputStream ins = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()))
final PruebaSingleton1 otro1 = ins.readObject()
final PruebaSingleton2 otro2 = ins.readObject()
println "$otro1 vs ${PruebaSingleton1.instance}"
assert !otro1.is(PruebaSingleton1.instance)
println "$otro2 vs ${PruebaSingleton2.instance}"
assert otro2.is(PruebaSingleton2.instance)

Imagen de Cid

Gracias duda resuelta

Gracias duda resuelta y eso del proxy automágico se podría hacer con RMI ? jajaja mejor ahi le paro ya son muchos escenarios jajajaja. Gracias.

Imagen de ezamudio

RMI

RMI usa la misma serialización que estoy usando en mi ejemplo para pasar parámetros y resultados, por lo que si devuelves un singleton como resultado de una llamada RMI o lo pasas como parámetro, entonces sí debes hacer lo de readResolve

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">