Preguntas de la entrevista de Complete Works of Java (7)

Preguntas de la entrevista de Complete Works of Java (7)

Baiyu es jaja

61. ¿De cuántas formas hay de escribir programas multiproceso?

Respuesta: Hay dos formas de implementar subprocesos múltiples antes de Java 5: una es heredar la clase Thread; la otra es implementar la interfaz Runnable. Ambos métodos deben anular el método run () para definir el comportamiento del hilo. Se recomienda este último, porque la herencia en Java es una herencia única. Una clase tiene una clase principal. Si hereda la clase Thread, no puede heredar otras clases. Obviamente, usar la interfaz Runnable es más flexible.

Suplemento: Después de Java 5, hay una tercera forma de crear un hilo: implementar la interfaz invocable, el método de llamada en esta interfaz puede generar un valor de retorno cuando finaliza la ejecución del hilo, el código es el siguiente:


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class MyTask implements Callable<Integer> {
    private int upperBounds;
    public MyTask(int upperBounds) {
        this.upperBounds = upperBounds;
    }
    @Override
    public Integer call() throws Exception {
        int sum = 0; 
        for(int i = 1; i <= upperBounds; i++) {
            sum += i;
        }
        return sum;
    }
}
class Test {
    public static void main(String[] args) throws Exception {
        List<Future<Integer>> list = new ArrayList<>();
        ExecutorService service = Executors.newFixedThreadPool(10);
        for(int i = 0; i < 10; i++) {
            list.add(service.submit(new MyTask((int) (Math.random() * 100))));
        }
        int sum = 0;
        for(Future<Integer> future : list) {
            // while(!future.isDone()) ;
            sum += future.get();
        }
        System.out.println(sum);
    }
}

62. ¿Cómo utilizar la palabra clave sincronizada?

Respuesta: La palabra clave sincronizada puede marcar objetos o métodos como sincronizados para lograr acceso mutuamente exclusivo a objetos y métodos. Puede usar sincronizado (objeto) {…} para definir un bloque de código sincronizado, o usar sincronizado como una modificación de método al declarar un método símbolo. El uso de la palabra clave sincronizada se ha demostrado en el ejemplo de la pregunta 60.

63. Dé ejemplos de sincrónico y asincrónico.

Respuesta: Si hay recursos críticos en el sistema (la cantidad de recursos es menor que la cantidad de subprocesos que compiten por los recursos), por ejemplo, los datos que se escriben pueden ser leídos por otro subproceso en el futuro, o los datos que se leen pueden haber sido escritos por otro subproceso. Luego, se debe acceder a estos datos de forma sincrónica (los bloqueos exclusivos en las operaciones de la base de datos son el mejor ejemplo). Cuando una aplicación llama a un método en un objeto que tarda mucho en ejecutarse y no quiere que el programa espere a que el método regrese, debe usar programación asincrónica. En muchos casos, es más eficiente usar métodos asincrónicos. De hecho, la llamada sincronización se refiere a operaciones de bloqueo y asincrónica se refiere a operaciones sin bloqueo.

64. Para iniciar un hilo, ¿llama al método run () o start ()?

Respuesta: Iniciar un hilo es llamar al método start () para hacer que el procesador virtual representado por el hilo esté en un estado ejecutable. Esto significa que puede ser programado y ejecutado por la JVM. Esto no significa que el hilo se ejecutará inmediatamente. El método run () es un método de devolución de llamada después de que se inicia el hilo.

65. ¿Qué es un grupo de subprocesos?

Respuesta: En la programación orientada a objetos, crear y destruir objetos requiere mucho tiempo, porque crear un objeto requiere acceso a recursos de memoria o más recursos. Esto es especialmente cierto en Java, donde la máquina virtual intentará rastrear cada objeto para que pueda ser recolectado como basura después de que el objeto sea destruido. Por lo tanto, una forma de mejorar la eficiencia del programa de servicio es reducir el número de creación y destrucción de objetos tanto como sea posible, especialmente la creación y destrucción de algunos objetos que consumen muchos recursos. Ésta es la razón de la tecnología de "recursos agrupados". El grupo de subprocesos, como su nombre lo indica, consiste en crear varios subprocesos ejecutables de antemano y ponerlos en un grupo (contenedor). Cuando necesite obtener subprocesos del grupo, no es necesario crearlos. No es necesario destruir los subprocesos, sino volver a colocarlos en el grupo cuando los necesite. El costo de destruir objetos de hilo.
La interfaz Executor en Java 5+ define una herramienta para ejecutar subprocesos. Su subtipo, interfaz de grupo de subprocesos, es ExecutorService. Es más complicado configurar un grupo de subprocesos, especialmente cuando el principio del grupo de subprocesos no es muy claro. Por lo tanto, se proporcionan algunos métodos de fábrica estáticos en el lado Ejecutores de la clase de herramienta para generar algunos grupos de subprocesos de uso común, como se muestra a continuación:

  • newSingleThreadExecutor: crea un grupo de subprocesos de un solo subproceso. Este grupo de subprocesos tiene solo un subproceso en funcionamiento, lo que equivale a un solo subproceso que realiza todas las tareas en serie. Si el único hilo termina de manera anormal, habrá un hilo nuevo para reemplazarlo. Este grupo de subprocesos garantiza que el orden de ejecución de todas las tareas se ejecute en el orden de envío de la tarea.
  • newFixedThreadPool: crea un grupo de subprocesos de tamaño fijo. Cada vez que se envía una tarea, se crea un hilo hasta que el hilo alcanza el tamaño máximo del grupo de hilos. Una vez que el tamaño del grupo de subprocesos alcanza el máximo, permanecerá sin cambios. Si un subproceso termina debido a una ejecución anormal, el grupo de subprocesos se complementará con un nuevo subproceso.
  • newCachedThreadPool: crea un grupo de subprocesos almacenable en caché. Si el tamaño del grupo de subprocesos excede los subprocesos necesarios para procesar las tareas, se reciclarán algunos subprocesos inactivos (sin realizar tareas durante 60 segundos). Cuando aumenta el número de tareas, el grupo de subprocesos puede agregar de forma inteligente nuevos subprocesos para procesar tareas. Este grupo de subprocesos no limita el tamaño del grupo de subprocesos. El tamaño del grupo de subprocesos depende completamente del tamaño máximo de subprocesos que el sistema operativo (o JVM) puede crear.
  • newScheduledThreadPool: crea un grupo de subprocesos de tamaño ilimitado. Este grupo de subprocesos es compatible con las necesidades de ejecución periódica y programada de tareas.
  • newSingleThreadExecutor: crea un grupo de subprocesos de un solo subproceso. Este grupo de subprocesos admite las necesidades de ejecución periódica y programada de tareas.
    El ejemplo en la pregunta 60 demuestra el código que crea un grupo de subprocesos a través de la clase de herramienta Ejecutores y utiliza el grupo de subprocesos para ejecutar subprocesos. Si desea utilizar el grupo de subprocesos en el servidor, se recomienda encarecidamente utilizar el método newFixedThreadPool para crear el grupo de subprocesos, de modo que pueda obtener un mejor rendimiento.

    66. ¿El estado básico de los hilos y la relación entre estados?

    responder:
    Preguntas de la entrevista de Complete Works of Java (7)

Nota: Ejecutando significa estado de ejecución, Ejecutable significa estado listo (todo está listo, solo debe CPU), Bloqueado significa estado de bloqueo, hay muchas situaciones en estado de bloqueo, que puede deberse a que se llama al método wait () para ingresar al grupo de espera, o puede ser La ejecución del método de sincronización o el bloque de código de sincronización ingresa al grupo de bloqueo en espera, o se llama al método sleep () o join () para esperar el final del sueño u otros subprocesos, o porque se produce una interrupción de E / S.

67. Describa brevemente las similitudes y diferencias entre sincronizado y java.util.concurrent.locks.Lock?

Respuesta: Lock es una nueva API introducida después de Java 5. En comparación con la palabra clave sincronizada, las principales similitudes son: Lock puede completar todas las funciones realizadas por sincronizado; la principal diferencia: Lock tiene una semántica de hilo más precisa y mejor que sincronizada Rendimiento, y no es obligatorio obtener un candado. Synchronized liberará automáticamente el bloqueo, y Lock debe requerir que el programador lo libere manualmente, y es mejor liberarlo en el bloque final (este es el mejor lugar para liberar recursos externos).

68. ¿Cómo realizar la serialización en Java y cuál es el significado?

Respuesta: La serialización es un mecanismo para procesar flujos de objetos. El llamado flujo de objetos es transmitir el contenido de los objetos. Puede leer y escribir los objetos transmitidos, y también puede transferir los objetos transmitidos entre redes. La serialización sirve para resolver los problemas que pueden ser causados ​​por las operaciones de lectura y escritura del flujo de objetos (los datos pueden estar fuera de orden si no están serializados).
Para lograr la serialización, debe hacer que una clase implemente la interfaz serializable, que es una interfaz de identificación, marcar que el objeto de esta clase se puede serializar y luego usar un flujo de salida para construir un flujo de salida de objeto y pasar el método writeObject (Object) Puede escribir el objeto de implementación (es decir, guardar su estado); si necesita deserializarlo, puede usar un flujo de entrada para crear un flujo de entrada de objeto y luego leer el objeto del flujo a través del método readObject. Además de lograr la persistencia del objeto, la serialización también se puede utilizar para la clonación profunda de objetos (consulte la pregunta 29).

69. ¿Cuántos tipos de flujos hay en Java?

Respuesta: flujo de bytes y flujo de caracteres. El flujo de bytes se hereda de InputStream y OutputStream, y el flujo de caracteres se hereda de Reader y Writer. Hay muchas otras transmisiones en el paquete java.io, principalmente para mejorar el rendimiento y la facilidad de uso. Hay dos puntos a tener en cuenta sobre Java I / O: uno es dos simetría (la simetría de entrada y salida, la simetría de bytes y caracteres); el otro son dos modos de diseño (modo adaptador y modo decoración). Además, el flujo en Java es diferente de C # en que tiene solo una dimensión y una dirección.

Preguntas de entrevista-Programación para realizar copia de archivo. (Este tema suele aparecer durante la prueba escrita, el siguiente código proporciona dos esquemas de implementación)


import java.io.FileInputStream;
import java.io.FileOutputStream;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public final class MyUtil {
    private MyUtil() {
        throw new AssertionError();
    }
    public static void fileCopy(String source, String target) throws IOException {
        try (InputStream in = new FileInputStream(source)) {
            try (OutputStream out = new FileOutputStream(target)) {
                byte[] buffer = new byte[4096];
                int bytesToRead;
                while((bytesToRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesToRead);
                }
            }
        }
    }
    public static void fileCopyNIO(String source, String target) throws IOException {
        try (FileInputStream in = new FileInputStream(source)) {
            try (FileOutputStream out = new FileOutputStream(target)) {
                FileChannel inChannel = in.getChannel();
                FileChannel outChannel = out.getChannel();
                ByteBuffer buffer = ByteBuffer.allocate(4096);
                while(inChannel.read(buffer) != -1) {
                    buffer.flip();
                    outChannel.write(buffer);
                    buffer.clear();
                }
            }
        }
    }
}

Nota : El TWR de Java 7. Después de usar TWR, no necesita liberar recursos externos finalmente, lo que hace que el código sea más elegante.

70. Escriba un método, ingrese un nombre de archivo y una cadena, y cuente el número de veces que esta cadena aparece en el archivo.

Respuesta: El código es el siguiente:


import java.io.BufferedReader;
import java.io.FileReader;
public final class MyUtil {
    // 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象(绝对好习惯)
    private MyUtil() {
        throw new AssertionError();
    }

    /**     * 统计给定文件中给定字符串的出现次数     *     * @param filename  文件名     * @param word 字符串     * @return 字符串在文件中出现的次数     */
    public static int countWordInFile(String filename, String word) {
        int counter = 0;
        try (FileReader fr = new FileReader(filename)) {
            try (BufferedReader br = new BufferedReader(fr)) {
                String line = null;
                while ((line = br.readLine()) != null) {
                    int index = -1;
                    while (line.length() >= word.length() && (index = line.indexOf(word)) >= 0) {
                        counter++;
                        line = line.substring(index + word.length());
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return counter;
    }
}

Supongo que te gusta

Origin blog.51cto.com/15061944/2593694
Recomendado
Clasificación