Saltar al contenido

¿Hay alguna forma de evitar la excepción ClosedByInterruptException?

Hola, hemos encontrado la solución a tu búsqueda, continúa leyendo y la obtendrás más abajo.

Solución:

Como dijiste que querías “un mapeo de memoria compartido entre subprocesos”, no existe tal problema en absoluto, ya que el mapeo de memoria no se ve afectado por el cierre de un FileChannel. De hecho, es una buena estrategia cerrar el canal lo antes posible para reducir los recursos que tiene la aplicación.

P.ej

static volatile boolean running = true;

public static void main(String[] args) throws IOException {
    Path name = Paths.get("delete.me");
    MappedByteBuffer mapped;
    try(FileChannel fc1 = FileChannel.open(name, READ,WRITE,CREATE_NEW,DELETE_ON_CLOSE)) 
        mapped = fc1.map(FileChannel.MapMode.READ_WRITE, 0, 4096);
    
    Thread thread1 = new Thread(() -> 
        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(50));
        while(running && !Thread.interrupted()) 
            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
            byte[] b = new byte[5];
            mapped.position(4000);
            mapped.get(b);
            System.out.println("read "+new String(b, StandardCharsets.US_ASCII));
        
    );
    thread1.setDaemon(true);
    thread1.start();
    Thread thread2 = new Thread(() -> 
        byte[] b = "HELLO".getBytes(StandardCharsets.US_ASCII);
        while(running && !Thread.interrupted()) 
            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
            mapped.position(4000);
            mapped.put(b);
            System.out.println("wrote "+new String(b, StandardCharsets.US_ASCII));
            byte b1 = b[0];
            System.arraycopy(b, 1, b, 0, b.length-1);
            b[b.length-1] = b1;
        
        mapped.force();
    );
    thread2.setDaemon(true);
    thread2.start();
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
    thread2.interrupt();
    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
    running = false;

Esto demuestra cómo los subprocesos pueden leer y escribir sus datos después de que el canal se ha cerrado y la interrupción del subproceso de escritura no detiene el subproceso de lectura.

Si necesitas realizar FileChannel operaciones además de las E / S mapeadas en memoria, no hay problema en el uso de múltiples FileChannel instancias, por lo que cerrar un canal no afecta al otro. P.ej

static volatile boolean running = true;

public static void main(String[] args) throws IOException 
    Path name = Paths.get("delete.me");
    try(FileChannel fc1 = FileChannel.open(name,READ,WRITE,CREATE_NEW,DELETE_ON_CLOSE);
        FileChannel fc2 = FileChannel.open(name,READ,WRITE)) 
        Thread thread1 = new Thread(() -> 
            LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(50));
            try 
                MappedByteBuffer mapped = fc1.map(FileChannel.MapMode.READ_WRITE, 0, 4096);
                while(running && !Thread.interrupted()) 
                    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
                    byte[] b = new byte[5];
                    mapped.position(4000);
                    mapped.get(b);
                    System.out.println("read from map "
                        +new String(b, StandardCharsets.US_ASCII)
                        +", file size "+fc1.size());
                
            catch(IOException ex) 
                ex.printStackTrace();
            
        );
        thread1.setDaemon(true);
        thread1.start();
        Thread thread2 = new Thread(() -> 
            byte[] b = "HELLO".getBytes(StandardCharsets.US_ASCII);
            try 
                MappedByteBuffer mapped = fc2.map(FileChannel.MapMode.READ_WRITE, 0, 4096);
                fc2.position(4096);
                try 
                    while(running && !Thread.interrupted()) 
                        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
                        mapped.position(4000);
                        mapped.put(b);
                        System.out.println("wrote to mapped "
                            +new String(b, StandardCharsets.US_ASCII));
                        byte b1 = b[0];
                        System.arraycopy(b, 1, b, 0, b.length-1);
                        b[b.length-1] = b1;
                        fc2.write(ByteBuffer.wrap(b));
                    
                 finally  mapped.force(); 
            catch(IOException ex) 
                ex.printStackTrace();
            
        );
        thread2.setDaemon(true);
        thread2.start();
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
        thread2.interrupt();
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
        running = false;
    

Aquí, la interrupción de un hilo cierra su canal, pero no afecta al otro. Además, incluso cuando cada hilo adquiere su propio MappedByteBuffer desde su propio canal, los cambios se transmiten al otro, incluso sin el uso de force(). Por supuesto, este último se define como un comportamiento dependiente del sistema, que no se garantiza que funcione en todos los sistemas.

Pero como se muestra con el primer ejemplo, aún puede crear búferes compartidos desde solo uno de los canales al principio, mientras realiza las operaciones de E / S en un canal diferente, uno por hilo, y no importa si y qué canales se cierra, los búferes mapeados no se ven afectados por él.

Puede utilizar la reflexión para acceder a la interruptor campo ilegalmente y consigue el sun.nio.ch.Interruptible tipo de clase desde allí para crear una instancia de proxy:

private void doNotCloseOnInterrupt(FileChannel fc) 
    try 
        Field field = AbstractInterruptibleChannel.class.getDeclaredField("interruptor");
        Class interruptibleClass = field.getType();
        field.setAccessible(true);
        field.set(fc, Proxy.newProxyInstance(
                interruptibleClass.getClassLoader(), 
                new Class[]  interruptibleClass ,
                new InterruptibleInvocationHandler()));
     catch (final Exception e) 
        Jvm.warn().on(getClass(), "Couldn't disable close on interrupt", e);
    


public class InterruptibleInvocationHandler implements InvocationHandler 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    
        // TODO: Check method and handle accordingly
        return null;
    

En Java9 esto funciona con una sola advertencia, ya que se ejecuta de forma predeterminada con --illegal-access=permit.

Sin embargo, esta bandera podría eliminarse en versiones futuras y la mejor manera de garantizar que esto funcione a largo plazo es usar la bandera --add-opens:

--add-opens java.base/sun.nio.ch=your-module
--add-opens java.base/java.nio.channels.spi=your-module

O, si no está trabajando con módulos (no recomendado):

--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/java.nio.channels.spi=ALL-UNNAMED

Esto funciona con Java 9, Java 10 y la actual compilación de acceso anticipado JDK 11 (28 (2018/8/23)).

Al usar un AsynchronousFileChannel, la excepción ClosedByInterruptException nunca se lanza Simplemente no parece preocuparse por la interrupción

Prueba realizada con jdk 1.8.0_72

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.atomic.AtomicLong;

public class A 
    static volatile boolean running = true;

    public static void main(String[] args) throws IOException, InterruptedException 
        String name = "delete.me";
        Path path = new File(name).toPath();
        AtomicLong position = new AtomicLong(0);

        AsynchronousFileChannel fc = AsynchronousFileChannel.open(path, 
                StandardOpenOption.CREATE_NEW, StandardOpenOption.DELETE_ON_CLOSE ,
                StandardOpenOption.READ, StandardOpenOption.WRITE,
                StandardOpenOption.WRITE, StandardOpenOption.SYNC);

        CompletionHandler handler =
                new CompletionHandler() 
                @Override
                public void completed(Integer result, Object attachment) 
                    //System.out.println(attachment + " completed with " + result + " bytes written");
                    position.getAndAdd(result);
                
                @Override
                public void failed(Throwable e, Object attachment) 
                    System.err.println(attachment + " failed with:");
                    e.printStackTrace();
                
            ;

        Runnable monitorRun = () -> 
            try 
                while (running) 
                    System.out.println(name + " is " + (fc.size() >> 10) + " KB");

                    try 
                        Thread.sleep(1000);
                     catch (InterruptedException e) 
                        System.out.println("Interrupted");
                        Thread.currentThread().interrupt();
                        System.out.println("Interrupt call failed so return");
                        return;
                    
                
             catch (IOException e) 
                System.err.println("Monitor thread died");
                e.printStackTrace();
            
        ;

        Thread monitor = new Thread(monitorRun);
        monitor.setDaemon(true);
        monitor.start();

        Thread writer = new Thread(() -> 
            ByteBuffer bb = ByteBuffer.allocateDirect(32);
            try 
                while (running) 
                    bb.position(0).limit(32);
                    fc.write(bb,position.get(),null,handler);

                    try 
                        Thread.sleep(10);
                     catch (InterruptedException e) 
                        System.out.println("Interrupted");
                        Thread.currentThread().interrupt();
                    
                
             catch (Exception e) 
                System.err.println("Writer thread died");
                e.printStackTrace();
            
        );

        writer.setDaemon(true);
        writer.start();

        Thread.sleep(5000);
        monitor.interrupt();
        Thread.sleep(2000);
        monitor = new Thread(monitorRun);
        monitor.start();
        Thread.sleep(5000);
        running = false;
        fc.close();
    

Genere la siguiente salida:

delete.me is 0 KB
delete.me is 3 KB
delete.me is 6 KB
delete.me is 9 KB
delete.me is 12 KB
Interrupted
Interrupt call failed so return
delete.me is 21 KB
delete.me is 24 KB
delete.me is 27 KB
delete.me is 30 KB
delete.me is 33 KB

Nos puedes añadir valor a nuestra información cooperando tu experiencia en los informes.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *