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

Problema con numeros Repetidos

Hola buenas tardes soy nuevo en esto de los foros y necesito la orienacion de ustedes, mi problema es el siguiente:

Me ecnuentro desarrollando una aplicacion web en java referente a la Facturacion Electronica, el detalle es que tengo una clase que se encarga de darme numeros de folios, estos folios son consecutivos, esta clase la trate de realizar aplicando en praton singleton para que me creara una instancia y de ese moto solamente aumentar un contador para poder sacar los numeros sonsecutivos, trabaja bien con pocos usuario pero se da el caso que cuando mas de trs usuarios en diferentes sessiones crean una factura y al momento de haer una peticion a mi clase foleadora entonces le da el mismo numero de folio y este folio se repite mas de una vez, espero me puedan orientar en como debo de hacerle para crear una clase que le brinde un numero consecutivo a toda aquuella peticion que se realize sobre la misma.

Saludos

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.

Quizá lo que crees que es un

Quizá lo que crees que es un singleton en realidad no lo es. O quizá la forma en la que estás devolviendo el consecutivo no es thread safe.

Postea tu código ( no todo, solo lo relevante, donde puedas reproducir el error, y recuerda ponerlo entre <code> ) para poder saberlo.

Este es mi codigo espero me pueda ayudar

public class FoliosSingleton {
    private static int contador;
    private static FoliosSingleton folioSingleton; //= new FoliosSingleton();
    private static Folios folioBD;
    private static xxxEmisorRemote serviceEmisor = MyServicelocator.getxxxEmisorRemote();
    private static xxxSeriesRemote serviceSeries = MyServicelocator.getxxSeriesRemote();
    private static capufeFoliosRemote serviceFolios = MyServicelocator.getxxxFoliosRemote();
    private static int opc;
    private static Series serie;
    private static List<Integer> listaFoleadora = new ArrayList<Integer>();

    public FoliosSingleton() {
    }

    private synchronized static void createInstance() {
        if (folioSingleton == null) {          
                folioSingleton = new FoliosSingleton();        
        }
    }

    public static FoliosSingleton getFoliosSingleton(String numeroserie) {

        try {
            createInstance();
            if (!listaFoleadora.isEmpty()) {
                System.out.println("LA LISTA CONTIERNE ALGUNOS OBJETOS getSIngletone--> " + listaFoleadora.size());
                for (Iterator<Integer> it = listaFoleadora.iterator(); it.hasNext(); ) {
                    Integer str = it.next();
                    folioSingleton.setContador(str);
                    it.remove();
                    break;
                } //
                System.out.println("TAMAÑO DEL LA LISTA DESPUES DE RETORNAR EL SINGLETON--> " + listaFoleadora.size());
            } else {
                if (opc == 0) {

                    Emisor emi = new Emisor();
                    emi = serviceEmisor.findbyID(new Integer(1)); //Con esto buscamos al Emisor
                    serie = serviceSeries.getfindEmisorNombre(emi, numeroserie);
                    folioBD = serie.getFolios();
                    contador = Integer.valueOf(folioBD.getContador()) + 1;
                    folioSingleton.setContador(contador);
                    opc++;
                } else {
                    contador++;
                    opc++;
                    folioSingleton.setContador(contador);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (listaFoleadora.isEmpty()) {
                if (opc == 5 && listaFoleadora.isEmpty()) {
                    Emisor emi = new Emisor();
                    emi = serviceEmisor.findbyID(new Integer(1)); //Con esto buscamos al Emisor
                    Series serie = serviceSeries.getfindEmisorNombre(emi, numeroserie);
                    serie.setContador("" + contador);
                    serviceSeries.mergeSeries(serie);
                    Folios folio = serie.getFolios();
                    folio.setContador("" + contador);
                    serviceFolios.mergeFolios(folio);
                    //folioSingleton = null;
                    folioBD = null;
                    serie = null;
                    opc = 0;
                    listaFoleadora.clear();
                }
            }

        }

        return folioSingleton;
    }

    public static boolean existevalor(int numero) {
      //Codigo
    }

    public static void addListaFoleadora(int fol) {
      //Codigo
    }

    public static void setContador(int contador) {
        FoliosSingleton.contador = contador;
    }

    public static int getContador() {
        return contador;
    }

    public static void setFolioBD(Folios folioBD) {
        FoliosSingleton.folioBD = folioBD;
    }

    public static Folios getFolioBD() {
        return folioBD;
    }

    public static void setSerie(Series serie) {
        FoliosSingleton.serie = serie;
    }

    public static Series getSerie() {
        return serie;
    }

}

Por cada 5 folios generados por la clase singleton entonces actualizo en la BD, pero el problema que he notado es que cuando varios usuarios conectados hacen una peticion la mismo tiempo le retorna el valor que lleve el contador es decir si dos a mas usuarios hacen la misma peticion entonces esta clase le da los mismos folios, tambien debo de aclarar que esto no sucede muy seguido pero debo de hacer que no se repitan los folios.

Imagen de ezamudio

secuencia

Usa una secuencia autoincremental en la base de datos y quítate de problemas, todo ese código lo cambias por un vil SELECT a la secuencia y nunca te va a dar números repetidos.

Ok lo implementare

Intentare con lo que tu me sugieres, antes debo de mencionarles que ya tengo una solucion pero no se si sea la ideal, se las comento antes de mandar a traeer la instancia del singleton ejecuto un retardo, para que cuando existan muchos usuario en sesion y quieran facturar al mismo tiempo no hagan la peticion al mismo tiempo asi esto impide que no se den los mismos nuemeros de folios, ya tengo correiendo esta funcionalidad desde ayer solo espero que no retrase la productividad. se las comparto:

            int segundos = (int)(Math.random() * 19 + 1);
            esperarXsegundos(segundos); //Esto es para que tenga un retraso en pedir un folio
            Integer folio = FoliosSingleton.getFoliosSingleton(serie.getNombre()).getContador();

Y este es el metodo de retraso

    private void esperarXsegundos(int segundos) {
        try {
            Thread.sleep(segundos * 100); //Milisegundos
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
    }
Imagen de chicowed

Retardo?, para eso tienes

Retardo?, para eso tienes synchronized

Sincronizar métodos nos permite prevenir inconsistencias cuando un objeto es accesible desde distintos hilos. Por ejemplo si un hilo incrementa el contador, mientras otro lo decrementa, y otro lo lee, puede que el resultado no sea el esperado si hay cierta concurrencia. Para eso las lecturas y escrituras de un objeto compartido, se hacen a traves de métodos sincronizados.

Esto tiene dos efectos:

No es posible invocar dos metodos sincronizados del mismo objeto a la vez. Si un metodo se esta ejecutando en un hilo, todos los hilos que invoquen a otro metodo se bloquean hasta que el primero termina.

Cuando se llama a un método sincronizado, se establece automaticamente una relacion happens-before con cualquier llamada a un método sincronizado de este objeto. Esto garantiza que cualquier cambio sobre el objeto es visible para cualquier hilo.
Esto es una buena base, aunque hay muchas cosas a tener en cuenta cuando hablamos de hilos y concurrencia, especialmente Deadlocks, bloqueos mutuos y ese tipo de complicaciones.

Imagen de ezamudio

retardo

Eso de meter un retardo debe estar entre las tres peores soluciones posibles.

Synchronized es mejor. No sé por qué no simplemente haces que el contador sea un AtomicInteger o AtomicLong, si de plano estás obstinado con continuar con esa implementación tan complicada para algo tan simple.

Ok muchas gracias me han

Ok muchas gracias me han servido de mucho sus aportaciones asi como tambien me han dado mejores soluciones gracias por la ayuda me ire por la opcion del contador que menciona ezamudio gracias

Synchronized trampoline!

Imagen de chicowed

jeje

Todo resumido en una sola imagen, muy buena jeje

Hola a todos tenian razon

Hola a todos tenian razon estaba haciendo mucho pancho por nada jejeje ya esta resuelto con lo de synchronized gracias ahora ya no se repiten mis folios, que bueno que existan foros como estos para resolver y agrandar las dudas a novatos como yo muchas gracias chicowed ezamudio
jpaul OscarRyz hasta la proxima.

Imagen de ezamudio

problemas

En el futuro cuando en vez de 3 usuarios tengas 300 y te digan que tu servicio es hiperlento, ya podrás cambiar de usar synchronized a alguna otra solución más eficiente, como usar una vil secuencia en base de datos o usar un AtomicLong.

Ok lo tendre en mente e

Ok lo tendre en mente e iniciare a hacerlo de esa manera muchas gracias ezamudio

Hola compañero @ezamudio he

Hola compañero @ezamudio he hecho mi contador con AtomicInteger y bueno ahora no se como hacerle para que la clase sea a nivel de aplicacion para que todos los usuarios al momento de consultar esta clase foleadora al mismo tiempo les retorne valores numericos diferentes ya que hice unas pruebas y hubo algunos casos en lo que retorno el mismo valor numerico o posiblemente este mal mi intento de solucion, ya que me base en el patron singleton para poder crear una unica instancia. y asi solo tomar el valor de AtomicInteger

public class SingletonAtomic {

    private AtomicInteger contador = new AtomicInteger(0);
    private static SingletonAtomic intancia;

    private SingletonAtomic() {
    }

    private static void createInstance() {
        if (intancia == null) {
            intancia = new SingletonAtomic();
        }
    }

    public static SingletonAtomic getFoliosSingleton() {
        createInstance();
        return intancia;
    }

    public int getContadorAtomico() {
        return contador.getAndIncrement();
    }

}

Pero te digo hice pruebas con varios usuarios, y al memento de facturar les regreso valores repetidos no se como hacerle
Asi lo mando a traer

Integer folio = SingletonAtomic.getFoliosSingleton().getContadorAtomico();
Imagen de ezamudio

no es thread-safe

Tu singleton no es thread-safe. Si dos hilos invocan getFoliosSingleton al mismo tiempo, es posible que ambos obtengan una instancia diferente, porque en createInstance() para ambos se cumple la condición instancia==null porque no se ha creado la instancia en ningún caso. Hay varios posts previos en este sitio donde se discute hasta la muerte el tema de los singletons en ambientes concurrentes. Ese es probablemente tu problema, porque el método getAndIncrement() es atómico, es decir si dos hilos lo invocan al mismo tiempo, a cada uno le va a dar un valor distinto.

Imagen de chicowed

Solución

Amigo tu solución esta por este lado, Auto Increment MySQL es recomendable que te apoyes con tu motor de DB.
Te evitas de muchos problemas.

Agradesco la ayuda el

Agradesco la ayuda el detalle es que bueno la logica es la siguiente

1. Creo la factura y le asingo el folio que lleve la base de datos
2. Se timbra, pero hay ocasiones en las que existen algunos errores entonces el folio se perderia, pero si temgo una forma de recuperarlo y asignarselo a otro usuario.
3. Si todo es correcto se inserta el una tabla llamada facturas, el folio, serie , valor, etc, pero todo va relacionado con respecto a mandar a timbrar la factura, ay que si existe un error pues no se deben de perder los folios, asi que bueno eso del auto increment no tengo ni idea de como lo usaria ya que te comento si se facturo correctamente entonces se inserta el folio consecutivo que lleva la factura creada,

Hola @ezamudio investigue al

Hola @ezamudio investigue al respecto con eso de thread-safe e hice cambios en mi codigo, espero que ya este bien me arias el favor de decirme si de esta manera esta bien , y bueno tambien me gustaria saber si el AtomicInteger debe de ser declarado como static o asi como esta en la clase

public class SingletonAtomic {

    private AtomicInteger contador = new AtomicInteger(0);
    private static SingletonAtomic instancia; //=new SingletonAtomic();

    private SingletonAtomic() {
    }

    public static SingletonAtomic getFoliosSingleton() {
        if (instancia == null) {
            synchronized (SingletonAtomic.class) {
                if (instancia == null) {                
                    instancia = new SingletonAtomic();
                }
            }
        }
        return instancia;
    }

    public int getContadorAtomico() {
        return contador.getAndIncrement();
    }

}

y bueno mando a llamar de la mima forma a getContadorAtomico

Integer folio = SingletonAtomic.getFoliosSingleton().getContadorAtomico();
Imagen de ezamudio

El doble if con el synchronized en medio, esa es una forma de garantizar que creas máximo una instancia. "Una sincronizada de synchronized, con todo por favor".

Otra forma es dentro de un inicializador estático, también anda por aquí en alguna discusión.

Re: Solución

Imagen de ezamudio

jajajajajajajJAJAJAJAJA

buenísimo! jajajja.

No veo como un AtomicInteger resuelve esa bronca, por cierto, de darle rollback o reusar folios no utilizados por algún error.

Si de verdad es indispensable que los folios sean seguidos, pues con más razón la solución es que venga el folio de la base de datos, y se genere dentro de la misma transacción donde se guarda la factura. Si ocurre un error *a ese nivel*, se da rollback a todo y no se incrementa el folio. La desventaja es que hace que todas las facturas generadas se guarden de manera serializada (no puede haber concurrencia en guardar facturas nuevas).

No me extrañaría que el SAT pidiera alguna tarugada irracional como eso de que los folios deban ser seguidos. Pero ps otra opción es usar el ID autoincremental y cuando haya un error, guardar el ID como una factura cancelada o algo así.

Imagen de chicowed

jajaja

y más jajaja muy buena xD

jajaja muy bueno, resuelto

jajaja muy bueno, resuelto con el AtomicInteger gracias, en lo que checo como hacerlo desde la BD, un saludo a todos y gracias por sus comentarios, y por la imagen jeje pero soy muy novato aun, creo yo que todos los buenos desarrolladores pasaron por la misma situacion jejejeje :P o talvez algo mas complejo :P

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