una seguridad por podido iniciar idse formada ejecucion desbloquear cómo configuracion con certificado bloqueado bloqueada aplicación aplicacion java multithreading concurrency stream

seguridad - no se ha podido iniciar la aplicacion java



¿Desbloquear un bloqueo de un hilo que no lo posee o rediseñarlo para evitarlo? (5)

Tengo un objeto de archivo que gestiona varias matrices de bytes y distribuye InputStream y OutputStream para leerlos y escribirlos. Cada matriz de bytes tiene un ReentrantReadWriteLock asociado.

El constructor de la subclase de InputStream que produce el archivo adquiere el bloqueo de los datos relevantes, mientras que close() libera el bloqueo. Ahora, el problema:

Supongamos que tengo una tarea que se ejecutará en otro subproceso que necesita información del archivo, se le da el InputStream como argumento antes de que comience, y es responsable de cerrar el flujo cuando termina. Estos requisitos parecen ser incompatibles con el mecanismo de bloqueo utilizado por mi archivo de datos, porque

  1. El InputStream se crea en un hilo diferente del que está cerrado.
  2. Un ReentrantReadWriteLock debe ser liberado por el hilo que lo posee.

Reescribir las partes del programa que esperan un InputStream como entrada para evitar el # 1 es inconveniente y hace que esas partes sean menos flexibles. Utilizar un tipo de bloqueo que permita el cambio de propiedad me permitiría evitar el # 2, pero no veo ningún bloqueo en la API de Java que pueda manejar eso y no estoy muy interesado en encontrar un bloqueo personalizado si no lo hago. Tengo que hacerlo.

¿Qué soluciones hay para este problema?


La forma más limpia de hacer esto es dar a los clientes algo más que una subclase de InputStream , incluso si esto significa un poco más de refactorización. En su diseño actual, no puede contar con que sus clientes liberen el bloqueo, ¿y si uno de ellos no lo hace? Esto no es una buena encapsulación.

Haga que el objeto de archivo administre los bloqueos y proporcione una API fácil para que sus clientes obtengan los datos . Incluso si esto significa cambiar algunas API.

El problema de los diferentes hilos que bloquean y liberan el mismo objeto es una clara señal de que tienes algo ligeramente mal en tu diseño actual. Trabaja en la refactorización mencionada anteriormente, te ayudará mucho más.


En función de tu respuesta a mi pregunta. Simplemente podría posponer la construcción (y el bloqueo) del InputStream pasando una clase personalizada utilizada para fabricar el InputStream al ThreadExecutorService . Luego, el hilo en el servicio solicita el InputStream desde este objeto que primero lo construye (ya que no existe) y entonces todo está listo.

Por ejemplo:

public interface InputStreamMaker { public InputStream getOrCreateInputStream(); }

Luego en tu clase principal que comienza todo:

static class InputStreamMakerImpl implements InputStreamMaker { private final Manager manager; // Your manager class (or use a normal Inner class on manager) private InputStream is; // other variables needed to define how to create input stream for the particular task here InputStreamMakerImpl(Manager manager) { this.manager = manager; } public InputStream getOrCreateInputStream() { if (is == null) { // pass this object - so manager will have all the details to create the right stream is = manager.createNewStream(this); } return is; } }

y luego en el hilo principal ...

...some method... InputStreamMakerImpl maker = new InputStreamMakerImpl(manager /* or this */); // set values needed in maker for your manager class to create the right input stream // start the worker and pass the InputStreamMaker (maker) as the parameter instead of the InputStream

Esta clase interna puede tener información adicional según sea necesario para crear la transmisión correcta para el trabajador. Ahora InputStream se crea / abre en el subproceso derecho y ese subproceso también puede cerrarlo y desbloquear el bloqueo.

Editar : Al volver a leer su pregunta, veo que es posible que no desee cambiar ninguno de los objetos Worker que ya toman InputStream . Entonces todavía puedes hacer esto por:

  1. Crear una subclase personalizada de InputStream que contenga todos los valores de lo que defino anteriormente como InputStreamMakerImpl .

  2. El InputStream personalizado tiene un método interno getInputStream() que hace lo que defino para getOrCreateInputStream() arriba.

  3. Entonces, todos los métodos para InputStream simplemente delegan las llamadas, por ejemplo:

    public int read() throws IOException { return getInputStream().read(); }


java.io.InputStream tiene un total de nueve métodos. Ya ha reemplazado un método ie close() .

Te recomendaría tener un bloqueo lento, es decir, no adquirir de forma agresiva el bloqueo en el constructor. Para esto, haga que ReentrantReadWriteLock esté disponible en su subclase InputStream y tenga un método privado llamado doAcquireLock() que intente adquirir el bloqueo. A continuación, anule los otros ocho métodos de InputStream manera que en todos los métodos reemplazados primero haga una llamada a doAcquireLock() y luego delegue la operación en super.

ejemplo:

public int read() doAcquireLock(); // This method get''s the lock return super.read(); }

Con este bloqueo se adquirirá el hilo que primero llama a cualquiera de los ocho métodos anteriores y será responsable de cerrarlo también.

Solo para asegurarse de que no termina en una situación de punto muerto que debe vigilar es asegurarse de que el cliente haga una llamada al método de cierre al final para liberar el bloqueo. Además, ninguno de los métodos debe invocarse después de llamar al método close mediante un indicador booleano para saber si la transmisión ya está cerrada (obsoleta).


Otra solución con la que tropecé al reflexionar sobre esto: una forma de ver el problema es que se debe a la reentrada de los bloqueos. ReentrantReadWriteLock realiza un seguimiento de la propiedad del bloqueo para gestionar la reentrada. Si deseo adquirir bloqueos en la creación de la transmisión y liberar bloqueos en el cierre de la transmisión, y cerrar las transmisiones en hilos que no sean aquellos en los que fueron creados, un bloqueo de reentrada no funcionará porque la secuencia errónea será propietaria de la secuencia de bloqueo.

Una forma de evitar esto es usar un bloqueo de conteo, pero forzar todo el bloqueo hacia afuera. Primero, el bloqueo de conteo: los estados son enteros -1,0,1, ...., donde -1 significa escritura, 0 significa desbloqueado y positivo n cuenta el número de lecturas concurrentes. De esta forma, las escrituras son aún exclusivas, pero las cerraduras no son específicas de hilos. Forzar todo bloqueo hacia afuera: Tenemos métodos como exists() donde necesitamos adquirir un bloqueo de lectura, pero exists() llamada a exists() mediante algunos métodos que ya tendrán un bloqueo de escritura. Por lo tanto, dividimos las agallas de exists() en un existsImpl() protegido, que debe existsImpl() solo cuando un bloqueo ya está retenido (es decir, internamente, por otros métodos *Impl() . De esta forma, una vez que se establece un bloqueo adquirido, solo se invocan los métodos Impl sin bloqueo, y evitamos la necesidad de reentrada.

Todo esto está muy bien, excepto que termina siendo extremadamente complejo para garantizar que no estás fastidiando en implementarlo --- llamando a un método de bloqueo público cuando deberías llamar a un protegido no bloqueado --- y creando un punto muerto. En segundo lugar, forzar todas las cerraduras hacia el exterior significa que estás reteniendo las cerraduras más tiempo, por lo que esto (probablemente) no es bueno para la concurrencia.

Entonces, esto es algo que intenté, pero decidí no usar.


Solución # 2:

Si no necesita mantener el bloqueo hasta el final del hilo, puede implementar su InputStream personalizado como se muestra a continuación, que puede proporcionar a sus clientes. Ahora, cuando un hilo necesita leerse, adquiere un bloqueo, luego lee y finalmente abandona automáticamente el bloqueo tan pronto como se completa la lectura. Su OutputStream personalizado se debe implementar de manera similar con el bloqueo de escritura.

.

public class LockingInputStream extends InputStream { private final InputStream byteArrayInputStream; private final Lock r; public LockingInputStream( InputStream byteArrayInputStream, ReentrantReadWriteLock rwl) { this.byteArrayInputStream = byteArrayInputStream; this.r = rwl.readLock(); } @Override public int read() throws IOException { r.lock(); try { return byteArrayInputStream.read(); } finally { r.unlock(); } } @Override public int available() throws IOException { r.lock(); try { return byteArrayInputStream.available(); } finally { r.unlock(); } } .... @Override public void close() throws IOException { r.lock(); try { byteArrayInputStream.close(); } finally { r.unlock(); } } }