¿Quieres una entrevista de varios subprocesos? ¡40 preguntas clásicas de entrevistas de múltiples subprocesos de grandes fábricas vienen a ayudar!

Escritas en el frente
, echemos un vistazo a estas 40 preguntas de entrevista de múltiples hilos de Ali y pruebe su propio nivel.

Inserte la descripción de la imagen aquí

1. ¿Cuál es el uso de multiproceso?

Una pregunta que puede parecer una tontería para muchas personas: usaré multi-threading, ¿para qué sirve? En mi opinión, esta respuesta es aún más absurda. El llamado "conocer la razón, conocer la razón", "poder usar" pero "conocer la razón", "por qué usar" es "conocer la razón", sólo en el grado de "conocer la razón y conocer la razón" Un punto de conocimiento se puede utilizar libremente. Bien, déjame hablar sobre mis puntos de vista sobre este tema:

  • Aproveche la CPU de varios núcleos

Con el avance de la industria, los portátiles, las computadoras de escritorio e incluso los servidores de aplicaciones comerciales actuales son al menos de doble núcleo, y no son infrecuentes los de 4, 8 o incluso 16 núcleos. Si es un programa de un solo subproceso, entonces en una CPU de doble núcleo El 50% se desperdició y el 75% se desperdició en una CPU de 4 núcleos. El llamado "subproceso múltiple" en una CPU de un solo núcleo es un subproceso múltiple falso. El procesador solo puede procesar una parte de la lógica al mismo tiempo, pero los subprocesos cambian más rápido y parece que se ejecutan varios subprocesos al mismo tiempo. El subproceso múltiple en una CPU de varios núcleos es el subproceso múltiple real. Permite que su lógica de varios segmentos funcione al mismo tiempo. El subproceso múltiple puede realmente aprovechar la CPU de varios núcleos y lograr el propósito de hacer un uso completo de la CPU.

  • Evitar el bloqueo

Desde la perspectiva de la eficiencia del programa, una CPU de un solo núcleo no solo no ofrece todas las ventajas del subproceso múltiple, sino que cambiará el contexto del subproceso debido a la ejecución de subprocesos múltiples en una CPU de un solo núcleo, lo que reduce la eficiencia general del programa. Pero para la CPU de un solo núcleo, todavía tenemos que usar subprocesos múltiples, solo para evitar el bloqueo. Imagínese que si una CPU de un solo núcleo usa un solo hilo, mientras el hilo esté bloqueado, por ejemplo, leyendo ciertos datos de forma remota, el par no ha regresado y no ha establecido un tiempo de espera, entonces todo su programa estará antes de que se devuelvan los datos. Dejó de correr. El subproceso múltiple puede prevenir este problema. Se están ejecutando varios subprocesos al mismo tiempo, incluso si la ejecución del código de un subproceso está bloqueada para leer datos, no afectará la ejecución de otras tareas.

  • Fácil de modelar

Ésta es otra ventaja que no es tan obvia. Suponga que hay una gran tarea A, programación de un solo subproceso, entonces hay muchas consideraciones y es problemático construir el modelo de programa completo. Sin embargo, si descomponemos una tarea A tan grande en varias tareas pequeñas, tarea B, tarea C y tarea D, establecemos modelos de programa por separado y ejecutamos estas tareas por separado a través de múltiples subprocesos, es mucho más simple.

2. La forma de crear hilos

Un problema más común, generalmente hay dos tipos:

(1) Heredar la clase Thread

(2) Implementar la interfaz ejecutable

En cuanto a cuál es mejor, no hace falta decir que este último es mejor, porque la forma de implementar interfaces es más flexible que la forma de heredar clases, y también puede reducir el grado de acoplamiento entre programas. La programación orientada a interfaces es también el núcleo de los seis principios de patrones de diseño.

3. La diferencia entre el método start () y el método run ()

Solo cuando se llame al método start (), mostrará las características del subproceso múltiple, y el código en el método run () de diferentes subprocesos se ejecutará alternativamente. Si simplemente llama al método run (), el código se ejecuta sincrónicamente y debe esperar a que el código del método run () de un hilo termine de ejecutarse antes de que otro hilo pueda ejecutar el código en el método run ().

4. La diferencia entre la interfaz ejecutable y la interfaz invocable

Hay una pregunta un poco profunda, y también veo la amplitud de conocimientos de un programador de Java.

El valor de retorno del método run () en la interfaz Runnable es nulo. Lo que hace es simplemente ejecutar el código en el método run (); el método call () en la interfaz Callable tiene un valor de retorno y es un tipo genérico. Y Future, FutureTask se puede utilizar para obtener los resultados de la ejecución asincrónica.

Esta es en realidad una característica muy útil, porque el multi-hilo es más difícil y más complicado que el de un solo hilo. Una razón importante es que el multi-hilo está lleno de incógnitas. ¿Se ha ejecutado un hilo? ¿Cuánto tiempo se ha ejecutado un hilo? ¿Se han asignado los datos que esperamos cuando se ejecuta un hilo? Es imposible saber que todo lo que podemos hacer es esperar a que se complete esta tarea de múltiples subprocesos. Callable + Future / FutureTask puede obtener los resultados de la operación multiproceso, y es realmente útil cancelar la tarea del hilo cuando el tiempo de espera es demasiado largo y no se obtienen los datos requeridos.

5. La diferencia entre CyclicBarrier y CountDownLatch

Se pueden usar dos clases aparentemente similares, ambas bajo java.util.concurrent, para indicar que el código se ejecuta hasta cierto punto. La diferencia entre las dos es:

  • Después de que un subproceso de CyclicBarrier se ejecuta hasta cierto punto, el subproceso deja de ejecutarse hasta que todos los subprocesos hayan alcanzado este punto, todos los subprocesos se vuelven a ejecutar; CountDownLatch no es, después de que un subproceso se ejecuta hasta cierto punto, simplemente Dar un cierto valor -1, el hilo continúa funcionando

  • CyclicBarrier solo puede evocar una tarea, CountDownLatch puede evocar múltiples tareas

  • CyclicBarrier se puede reutilizar, CountDownLatch no es reutilizable, el valor de conteo es 0 y CountDownLatch ya no se puede usar

6, el papel de la palabra clave volátil

Un tema muy importante es que todo programador de Java que aprenda y aplique multihilo debe dominarlo. El requisito previo para comprender el papel de la palabra clave volátil es comprender el modelo de memoria de Java. El modelo de memoria de Java no se analiza aquí. Puede consultar el punto 31. La palabra clave volátil tiene dos funciones principales:

  • El multiproceso gira principalmente en torno a las dos características de visibilidad y atomicidad. Las variables modificadas con la palabra clave volátil aseguran su visibilidad entre múltiples subprocesos. Es decir, cada vez que se lee una variable volátil, debe ser el último dato

  • El nivel inferior de ejecución del código no es tan simple como el lenguaje de alto nivel que hemos visto —- programa Java. Su ejecución es código Java—> bytecode—> ejecuta el código C / C ++ correspondiente según el bytecode—> Se compila el código C / C ++ Lenguaje ensamblador -> interactuar con circuitos de hardware En realidad, la JVM puede reordenar las instrucciones para obtener un mejor rendimiento, y pueden ocurrir algunos problemas inesperados con el subproceso múltiple. El uso de volatile reordenará la semántica prohibida, por supuesto, esto también reduce la eficiencia de ejecución del código hasta cierto punto

Desde un punto de vista práctico, una función importante de volatile es combinar con CAS para asegurar la atomicidad. Para obtener más detalles, consulte las clases del paquete java.util.concurrent.atomic, como AtomicInteger.

7. ¿Qué es la seguridad para subprocesos?

También es una pregunta teórica. Hay muchas respuestas diferentes. Personalmente creo que la mejor explicación: si su código se ejecuta en varios subprocesos y se ejecuta en un solo subproceso, siempre obtendrá el mismo resultado. Entonces su código es seguro para subprocesos.

Vale la pena mencionar este problema, es decir, hay varios niveles de seguridad de subprocesos:

  • Inmutable

Al igual que String, Integer y Long, todos son tipos finales. Ningún hilo puede cambiar sus valores. Deben cambiarse a menos que se haya creado uno nuevo. Por lo tanto, estos objetos inmutables se pueden utilizar directamente en un entorno multiproceso sin ningún medio de sincronización. utilizar

  • Absolutamente seguro para subprocesos

Independientemente del entorno de ejecución, la persona que llama no necesita medidas de sincronización adicionales. Hacer esto generalmente requiere muchos costos adicionales. Java se llama a sí mismo una clase segura para subprocesos. De hecho, la mayoría de ellas no son seguras para subprocesos. Sin embargo, hay clases absolutamente seguras para subprocesos en Java, como CopyOnWriteArrayList y CopyOnWriteArraySet.

  • Relativamente seguro para subprocesos

La seguridad relativa de subprocesos es lo que llamamos seguridad de subprocesos en el sentido habitual. Al igual que Vector, los métodos de agregar y quitar son todas operaciones atómicas y no se interrumpirán, sino solo para esto. Si un subproceso atraviesa un Vector , Hay un hilo en el agregar este Vector al mismo tiempo, el 99% de los casos aparecerá ConcurrentModificationException, que es el mecanismo de falla rápida.

  • Subproceso inseguro

No hay nada que decir sobre esto, ArrayList, LinkedList, HashMap, etc.son todas clases inseguras para subprocesos

8. Cómo obtener el archivo de volcado de subprocesos en Java

Para problemas como bucles infinitos, interbloqueos, bloqueo, apertura lenta de la página, etc., presionar el volcado de hilo es la mejor manera de resolver el problema. El llamado volcado de subprocesos es la pila de subprocesos. Hay dos pasos para obtener la pila de subprocesos:

  • Para obtener el pid del hilo, puede usar el comando jps, en el entorno Linux, también puede usar ps -ef | grep java

  • Para imprimir la pila de subprocesos, puede usar el comando jstack pid o kill -3 pid en el entorno Linux

Otro punto es que la clase Thread proporciona un método getStackTrace () que también se puede utilizar para obtener la pila de hilos. Este es un método de instancia, por lo que este método está vinculado a una instancia de subproceso específico. Cada vez que obtiene la pila de un subproceso específico en ejecución,

9. ¿Qué sucede si un hilo tiene una excepción de tiempo de ejecución?

Si no se detecta la excepción, el subproceso deja de ejecutarse. Otro punto importante es: si este hilo sostiene el monitor de un determinado objeto, entonces el monitor del objeto se liberará inmediatamente

10. Cómo compartir datos entre dos hilos

Es suficiente compartir objetos entre hilos y luego invocar y esperar esperar / notificar / notificar Todo, esperar / señal / señal Todos, por ejemplo, la cola de bloqueo BlockingQueue está diseñada para compartir datos entre hilos
Inserte la descripción de la imagen aquí

11. ¿Cuál es la diferencia entre el método de dormir y el método de espera?

Esta pregunta se hace a menudo. Tanto el método de suspensión como el método de espera se pueden usar para dejar la CPU durante un cierto período de tiempo. La diferencia es que si el subproceso sostiene el monitor de un objeto, el método de suspensión no abandonará el monitor de este objeto y el método de espera lo abandonará. Monitor de objetos

12. ¿Cuál es el papel del modelo productor-consumidor?

Esta pregunta es muy teórica, pero muy importante:

  • Mejorar la eficiencia operativa de todo el sistema equilibrando la capacidad de producción del productor y la capacidad de consumo del consumidor. Este es el papel más importante del modelo productor-consumidor.

  • Desacoplamiento, que es un efecto incidental del modelo productor-consumidor El desacoplamiento significa que hay menos conexiones entre productores y consumidores, y cuantas menos conexiones, más pueden desarrollarse independientemente sin restricciones mutuas.

13, para que sirve ThreadLocal

En pocas palabras, ThreadLocal es una forma de cambiar el espacio por el tiempo. En cada Thread, se mantiene un ThreadLocal.ThreadLocalMap implementado por el método de dirección abierta. Los datos se aíslan y los datos no se comparten. Naturalmente, no hay problema de seguridad de los hilos.

14. ¿Por qué se deben llamar al método wait () y al método notificar () / notificar a todos () en un bloque sincronizado?

Esto es obligatorio por el JDK. Tanto el método wait () como el método notify () / notifyAll () deben obtener el bloqueo de objeto antes de llamar

15. ¿Cuál es la diferencia entre el método wait () y el método notify () / notifyAll () al renunciar al monitor de objetos?

La diferencia entre el método wait () y el método notificar () / notificarAll () cuando se abandona el monitor de objetos es: el método esperar () libera el monitor de objetos inmediatamente, y el método notificar () / notificar a todos () espera que el código restante del hilo termine de ejecutarse Dejará el monitor de objetos.

16. Por qué utilizar el grupo de subprocesos

Evite la creación y destrucción frecuentes de hilos y consiga la reutilización de objetos de hilo. Además, el grupo de subprocesos también se puede utilizar para controlar de forma flexible el número de proyectos simultáneos.

17. Cómo detectar si un hilo contiene un monitor de objeto

También vi una pregunta de entrevista de varios subprocesos en Internet para saber que hay una manera de determinar si un subproceso contiene un monitor de objeto: la clase Thread proporciona un método HoldLock (Object obj), si y solo si el monitor del objeto obj es Devolverá verdadero cuando lo sostenga un hilo. Tenga en cuenta que este es un método estático, lo que significa que "un hilo" se refiere al hilo actual.

18. La diferencia entre sincronizado y ReentrantLock

Synchronized es la misma palabra clave que if, else, for, while, y ReentrantLock es una clase, que es la diferencia esencial entre los dos. Dado que ReentrantLock es una clase, proporciona características cada vez más flexibles que sincronizadas. Puede ser heredado, puede tener métodos y puede tener una variedad de variables de clase. ReentrantLock es más extensible que sincronizado en varios aspectos:

  • ReentrantLock puede establecer el tiempo de espera para adquirir el bloqueo, a fin de evitar un punto muerto

  • eentrantLock puede obtener información sobre varios bloqueos

  • ReentrantLock puede implementar de manera flexible múltiples notificaciones

Además, el mecanismo de bloqueo de los dos es realmente diferente. La capa inferior de ReentrantLock llama al método de estacionamiento inseguro para bloquear, y la operación sincronizada debe ser la palabra de marca en el encabezado del objeto. No estoy seguro de esto.

19. ¿Cuál es la concurrencia de ConcurrentHashMap?

La concurrencia de ConcurrentHashMap es el tamaño del segmento. El valor predeterminado es 16, lo que significa que hasta 16 subprocesos pueden operar ConcurrentHashMap al mismo tiempo. Esta es también la mayor ventaja de ConcurrentHashMap a Hashtable. En cualquier caso, Hashtable puede tener dos subprocesos para obtener Hashtable al mismo tiempo. ¿Datos?

20, que es ReadWriteLock

En primer lugar, para que quede claro, no es que ReentrantLock sea malo, pero ReentrantLock tiene algunas limitaciones. Si usa ReentrantLock, puede ser para evitar la inconsistencia de datos causada por el hilo A escribiendo datos y el hilo B leyendo datos, pero de esta manera, si el hilo C está leyendo datos, el hilo D también está leyendo datos, la lectura de datos no cambiará los datos, no es necesario Bloqueando, pero aún bloqueando, reduciendo el rendimiento del programa.

Debido a esto, nació ReadWriteLock. ReadWriteLock es una interfaz de bloqueo de lectura y escritura. ReentrantReadWriteLock es una implementación específica de la interfaz ReadWriteLock, que se da cuenta de la separación de lectura y escritura. El bloqueo de lectura es compartido y el bloqueo de escritura es exclusivo. No habrá exclusión mutua entre lectura y escritura. Escribir y leer, y escribir y escribir son mutuamente excluyentes, lo que mejora el rendimiento de la lectura y la escritura.

21. ¿Qué es FutureTask?

En realidad, esto se mencionó antes, FutureTask representa una tarea de operación asincrónica. Se puede pasar una clase de implementación específica de Callable a FutureTask, que puede esperar a obtener el resultado de esta tarea de operación asincrónica, determinar si se ha completado y cancelar la tarea. Por supuesto, dado que FutureTask también es una clase de implementación de la interfaz Runnable, FutureTask también se puede colocar en el grupo de subprocesos.

22. Cómo encontrar qué hilo usa la CPU más larga en el entorno Linux

Este es un problema más práctico y creo que este tipo de problema es bastante significativo. Puedes hacerlo:

  • Obtenga el pid, jps o ps -ef | grep java del proyecto, como he mencionado antes

  • top -H -p pid, el orden no se puede cambiar

De esta forma, puede imprimir el proyecto actual y el porcentaje de tiempo de CPU que ocupa cada hilo. Tenga en cuenta que lo que se escribe aquí es el LWP, que es el número de subproceso del subproceso nativo del sistema operativo. Mi computadora portátil no implementa el proyecto Java en el entorno Linux, por lo que no hay forma de tomar una captura de pantalla para demostrarlo. Si la empresa usa el entorno Linux para implementar el proyecto, puede intentar un poco.

Utilice "top -H -p pid" + "jps pid" para encontrar fácilmente la pila de subprocesos de un subproceso con un uso elevado de CPU y localizar la causa del uso elevado de CPU. Generalmente, las operaciones de código incorrectas conducen a bucles infinitos.

Por último, el LWP escrito en "top -H -p pid" está en decimal, y el número de hilo local escrito en "jps pid" está en hexadecimal. Después de una conversión, puede localizar el hilo con un alto uso de CPU. La pila de subprocesos actual está arriba.
Inserte la descripción de la imagen aquí

23, la programación Java escribe un programa que provocará un punto muerto

La primera vez que vi este tema, pensé que era una muy buena pregunta. Mucha gente sabe lo que es un interbloqueo: el hilo A y el hilo B esperan los bloqueos del otro, lo que hace que el programa se repita sin cesar. Por supuesto, se limita a esto. No sé cómo escribir un programa de interbloqueo. En este caso, no entiendo qué es un interbloqueo. Si entiendo una teoría, se acabó. En la práctica, nos encontramos con problemas de interbloqueo. Básicamente es invisible.

Para comprender realmente qué es un punto muerto, este problema no es difícil, solo unos pocos pasos:

  • Los dos subprocesos contienen dos objetos Object: lock1 y lock2. Estos dos bloqueos se utilizan como bloqueos para bloques de código de sincronización;

  • El bloque de código de sincronización en el método run () del hilo 1 primero adquiere el bloqueo de objeto de lock1, Thread.sleep (xxx), no toma mucho tiempo, 50 milisegundos es casi lo mismo y luego adquiere el bloqueo de objeto de lock2. El propósito principal de esto es evitar que el hilo 1 adquiera los bloqueos de objeto de lock1 y lock2 a la vez.

  • Thread 2 run) (El bloque de código de sincronización en el método primero adquiere el bloqueo de objeto de lock2, y luego adquiere el bloqueo de objeto de lock1. Por supuesto, el bloqueo de objeto de lock1 ha sido retenido por el bloqueo de thread 1, y el thread 2 debe esperar a que el thread 1 libere lock1. Bloqueo de objetos

De esta manera, el hilo 1 "duerme" después de dormir, el hilo 2 ha adquirido el bloqueo de objeto de lock2 y el hilo 1 intenta adquirir el bloqueo de objeto de lock2 en este momento y se bloquea. En este momento, se forma un interbloqueo. El código no está escrito, ocupa mucho espacio Java Multithreading 7: Deadlock Este artículo contiene la implementación del código de los pasos anteriores.

24, como despertar un hilo bloqueado

Si el hilo está bloqueado al llamar a wait (), sleep () o join (), puede interrumpir el hilo y reactivarlo lanzando InterruptedException; si el hilo encuentra bloqueo de IO, no hay nada que pueda hacer, porque IO es el sistema operativo Al darse cuenta, no hay forma de que el código Java toque directamente el sistema operativo.

25. ¿Cómo ayudan los objetos inmutables al multiproceso?

Un problema mencionado anteriormente es que los objetos inmutables aseguran la visibilidad de los objetos en la memoria, y la lectura de objetos inmutables no requiere medios de sincronización adicionales, lo que mejora la eficiencia de ejecución del código.

26. ¿Qué es el cambio de contexto de subprocesos múltiples?

El cambio de contexto de subprocesos múltiples se refiere al proceso en el que el control de la CPU se cambia de un subproceso que ya está en ejecución a otro subproceso que está listo y esperando obtener los derechos de ejecución de la CPU.

27. ¿Qué sucede si la cola del grupo de subprocesos está llena cuando envía una tarea?

Si usa LinkedBlockingQueue, que es una cola ilimitada, no importa. Continúe agregando tareas a la cola de bloqueo para esperar la ejecución, porque LinkedBlockingQueue se puede considerar como una cola infinita, que puede almacenar tareas infinitamente; si usa una cola limitada, por ejemplo En el caso de ArrayBlockingQueue, las tareas se agregarán primero a ArrayBlockingQueue. Cuando ArrayBlockingQueue está lleno, la política de rechazo RejectedExecutionHandler se utilizará para procesar las tareas completas. El valor predeterminado es AbortPolicy.

28. ¿Cuál es el algoritmo de programación de subprocesos utilizado en Java?

Con derecho preferente. Después de que un subproceso se quede sin CPU, el sistema operativo calculará una prioridad total en función de la prioridad del subproceso, la falta de subprocesos y otros datos y asignará el siguiente segmento de tiempo a un subproceso para su ejecución.

29, cuál es el papel de Thread.sleep (0)

Esta pregunta está relacionada con la anterior y estoy conectado. Debido a que Java usa un algoritmo de programación de subprocesos preventivo, puede suceder que un subproceso a menudo obtenga el control de la CPU. Para permitir que algunos subprocesos con menor prioridad también obtengan el control de la CPU, puede usar Thread.sleep ( 0) Activa manualmente una operación del sistema operativo para asignar segmentos de tiempo, que también es una operación para equilibrar el control de la CPU.

30. ¿Qué es el giro?

Muchos códigos sincronizados son códigos simples y el tiempo de ejecución es muy rápido.En este momento, los hilos en espera están bloqueados puede ser una operación que no valga la pena, porque el bloqueo de hilos implica cambiar entre el modo de usuario y el modo kernel. Dado que el código sincronizado se ejecuta muy rápido, deje que el hilo que espera el bloqueo no se bloquee, pero haga un bucle ocupado en el límite de sincronizado, que es giro. Si realiza varios ciclos ocupados y descubre que no se ha obtenido el bloqueo, vuelva a bloquearlo. Esta puede ser una mejor estrategia.

31. ¿Qué es el modelo de memoria Java?

El modelo de memoria Java define una especificación para el acceso multiproceso a la memoria Java. Explicar completamente el modelo de memoria de Java no es algo que se pueda decir claramente en unas pocas oraciones aquí, permítanme resumir brevemente varias partes del modelo de memoria de Java:

  • El modelo de memoria Java divide la memoria en memoria principal y memoria de trabajo. El estado de la clase, es decir, las variables compartidas entre clases, se almacena en la memoria principal.Cada vez que un hilo de Java usa estas variables en la memoria principal, leerá las variables en la memoria principal una vez y dejará que estas memorias existan. Hay una copia en su memoria de trabajo.Cuando ejecuta su propio código de hilo, usa estas variables y manipula la copia en su memoria de trabajo. Después de que se ejecuta el código del hilo, el último valor se actualizará en la memoria principal

  • Se definen varias operaciones atómicas para manipular variables en la memoria principal y la memoria de trabajo

  • Define las reglas para el uso de variables volátiles

  • Sucede antes, el principio de primera ocurrencia, define algunas reglas de que la operación A debe ocurrir antes de la operación B. Por ejemplo, en el mismo hilo, el código antes del flujo de control debe ocurrir antes que el código detrás del flujo de control, y una acción para liberar el bloqueo y desbloqueo Debe suceder antes de la acción de bloqueo para el mismo bloqueo, etc. Siempre que se cumplan estas reglas, no se requieren medidas de sincronización adicionales. Si un fragmento de código no cumple con todas las reglas de pasa antes, el fragmento de código debe ser un hilo Inseguro

32. Qué es CAS

CAS, el nombre completo es Comparar e intercambiar, es decir, comparar-reemplazar. Supongamos que hay tres operandos: el valor de memoria V, el antiguo valor esperado A y el valor que se va a modificar B. Si y solo si el valor esperado A y el valor de memoria V son iguales, el valor de memoria se modificará a B y devolverá verdadero, de lo contrario, ¿qué No hacer nada y devolver falso. Por supuesto, CAS debe cooperar con variables volátiles, a fin de garantizar que la variable obtenida cada vez sea el último valor en la memoria principal, de lo contrario, el antiguo valor esperado A siempre será un valor constante A para un determinado hilo, siempre que Si una operación CAS falla, nunca tendrá éxito.

33. ¿Qué es el bloqueo optimista y el bloqueo pesimista?

  • Bloqueo optimista: al igual que su nombre, es optimista sobre los problemas de seguridad de subprocesos causados ​​por operaciones simultáneas. El bloqueo optimista cree que la competencia no siempre ocurre, por lo que no es necesario mantener el bloqueo. Compare-reemplace estas dos acciones como Una operación atómica intenta modificar las variables en la memoria, si falla, significa que hay un conflicto y debe haber una lógica de reintento correspondiente.

  • Bloqueo pesimista: aún como su nombre, es pesimista sobre los problemas de seguridad de subprocesos causados ​​por operaciones concurrentes. El bloqueo pesimista cree que la competencia siempre ocurrirá, por lo que cada vez que se opera un recurso, tendrá un bloqueo exclusivo. Al igual que sincronizado, no importa el tres siete veintiuno, puede operar el recurso directamente cuando lo bloquea.

34. ¿Qué es AQS?

Hable brevemente sobre AQS, el nombre completo de AQS es AbstractQueuedSychronizer, que debe traducirse como sincronizador de cola abstracto.

Si la base de java.util.concurrent es CAS, entonces AQS es el núcleo de todo el paquete concurrente de Java y se utiliza en ReentrantLock, CountDownLatch, Semaphore, etc. En realidad, AQS conecta todas las entradas en forma de una cola de dos vías. Por ejemplo, ReentrantLock, todos los subprocesos en espera se colocan en una entrada y se conectan en una cola de dos vías. Si el subproceso anterior usa ReentrantLock, la cola de dos vías es en realidad la primera La entrada comienza a correr.

AQS define todas las operaciones en colas bidireccionales, pero solo abre los métodos tryLock y tryRelease para que los utilicen los desarrolladores. Los desarrolladores pueden reescribir los métodos tryLock y tryRelease de acuerdo con su propia implementación para lograr sus propias funciones concurrentes.

35. Seguridad del hilo del modo singleton

Es una pregunta común Lo primero que hay que decir es que la seguridad de subprocesos del modo singleton significa que una instancia de una determinada clase solo se creará una vez en un entorno multiproceso. Hay muchas formas de escribir el modo singleton, permítanme resumir:

  • Patrón de singleton estilo chino hambriento: seguridad de hilo

  • Escritura de patrón singleton de estilo perezoso: no es seguro para subprocesos

  • La redacción del modo singleton de bloqueo de doble verificación: seguridad de hilo

36. ¿Cuál es el papel de Semaphore?

El semáforo es un semáforo y su función es limitar el número de bloques de código concurrentes. El semáforo tiene un constructor que puede pasar un entero entero n, lo que significa que un cierto fragmento de código solo puede ser accedido por un máximo de n subprocesos. Si excede n, espere hasta que cierto subproceso termine de ejecutar este bloque de código y el siguiente subproceso Volver a entrar en. Se puede ver que si el entero de tipo int n = 1 pasado en el constructor Semaphore, equivale a convertirse en sincronizado.
Inserte la descripción de la imagen aquí

37. Claramente, hay solo una declaración "recuento de retorno" en el método size () de Hashtable, ¿por qué necesitamos sincronizar?

Esta es mi confusión anterior, no sé si ha pensado en este tema. Si hay varias declaraciones en un método, y todas están operando en la misma variable de clase, entonces ningún bloqueo en un entorno multiproceso conducirá inevitablemente a problemas de seguridad de subprocesos. Esto es fácil de entender, pero el método size () claramente tiene solo una declaración , ¿Por qué tenemos que bloquear?

Hay dos razones principales para comprender este problema trabajando y estudiando lentamente:

  • Solo un subproceso puede ejecutar métodos sincronizados de una clase fija a la vez, pero para los métodos no sincronizados de una clase, varios subprocesos pueden acceder a él al mismo tiempo. Por lo tanto, hay un problema. Tal vez el hilo A esté ejecutando el método put de Hashtable para agregar datos, el hilo B normalmente puede llamar al método size () para leer el número de elementos actuales en Hashtable, y el valor leído puede no ser el último Es posible que el hilo A haya terminado de agregar datos, pero el hilo B ya ha leído el tamaño sin que coincida con el tamaño ++, por lo que el tamaño leído por el hilo B debe ser inexacto. Después de agregar sincronización al método size (), significa que el hilo B llama al método size () solo después de que el hilo A llama al método put, lo que garantiza la seguridad del hilo.

  • La CPU ejecuta código, no código Java. Esto es muy importante y debes recordarlo. El código Java finalmente se traduce en código ensamblador para su ejecución, y el código ensamblador es el código que realmente puede interactuar con los circuitos de hardware. Incluso si ve que solo hay una línea de código Java, e incluso si ve que solo hay una línea de código de bytes generada después de que se compila el código Java, no significa que solo hay una operación de esta oración para la capa inferior. Se asume que una oración "recuento de retorno" se traduce en tres oraciones de ensamblaje y se ejecuta Es muy posible que el hilo cambie después de que se ejecute la primera oración.

38. ¿A qué hilo se llama el constructor de la clase del hilo y el bloque estático?

Esta es una pregunta muy engañosa y astuta. Recuerde: el método de construcción y el bloque estático de la clase de subproceso son llamados por el subproceso donde se encuentra la clase de subproceso new, mientras que el código en el método de ejecución es llamado por el subproceso mismo.

Si la declaración anterior le confunde, permítame darle un ejemplo. Suponga que Thread1 es nuevo en Thread2 y Thread2 es nuevo en la función principal. Entonces:

  • El método de construcción y el bloque estático de Thread2 son llamados por el hilo principal, y el método run () de Thread2 es llamado por el propio Thread2.

  • El método de construcción y el bloque estático de Thread1 son llamados por Thread2, y el método run () de Thread1 es llamado por el propio Thread1.

39. ¿Cuál es la mejor opción de método de sincronización y bloque de sincronización?

Bloque síncrono, lo que significa que el código fuera del bloque síncrono se ejecuta de forma asincrónica, lo que mejora la eficiencia del código más que todo el método de sincronización. Por favor, conozca un principio: cuanto menor sea el alcance de la sincronización, mejor.

Con este artículo, me gustaría mencionar que, aunque cuanto menor sea el alcance de la sincronización, mejor, todavía existe un método de optimización llamado acortamiento de bloqueo en la máquina virtual Java, que consiste en aumentar el alcance de la sincronización. Esto es útil. Por ejemplo, StringBuffer es una clase segura para subprocesos. Naturalmente, el método append () más comúnmente utilizado es un método de sincronización. Cuando escribimos código, agregaremos cadenas repetidamente, lo que significa bloqueos repetidos. -> Desbloquear, que es perjudicial para el rendimiento, porque significa que la máquina virtual Java tiene que cambiar repetidamente entre el modo kernel y el modo de usuario en este hilo, por lo que la máquina virtual Java bloquea el código de múltiples llamadas al método append. La operación aproximada extiende múltiples operaciones de adición al principio y al final del método de adición y se convierte en un gran bloque de sincronización, lo que reduce el número de bloqueos -> desbloqueos y mejora efectivamente la eficiencia de la ejecución del código.

40. ¿Cómo usar grupos de subprocesos para empresas con alta concurrencia y tiempo de ejecución de tareas corto? ¿Cómo utilizan los grupos de subprocesos las empresas con baja concurrencia y tiempo de ejecución de tareas prolongado? ¿Cómo utilizar el grupo de subprocesos para empresas con alta concurrencia y tiempo de ejecución empresarial prolongado?

Esta es una pregunta que vi en el sitio web de programación concurrente. Puse esta pregunta en la última. Espero que todos puedan verla y pensar en ella, porque esta pregunta es muy buena, muy práctica y muy profesional. Con respecto a este tema, mi opinión personal es:

  • Para servicios con alta simultaneidad y tiempo de ejecución de tareas corto, la cantidad de subprocesos en el grupo de subprocesos se puede establecer en la cantidad de núcleos de CPU + 1, lo que reduce el cambio de contexto de subprocesos

  • Deben distinguirse las empresas con baja concurrencia y tiempo de ejecución de tareas prolongado:

a) Si el tiempo del negocio se concentra durante mucho tiempo en operaciones de E / S, es decir, tareas intensivas de E / S, debido a que las operaciones de E / S no ocupan la CPU, no deje que todas las CPU estén inactivas, puede aumentar el número de subprocesos en el grupo de subprocesos para permitir que la CPU Maneje más negocios

b) Si el tiempo del negocio es largo y se concentra en operaciones informáticas, es decir, tareas computacionalmente intensivas, no hay forma de hacerlo. Igual que (1), establezca el número de subprocesos en el grupo de subprocesos para que sea menor, reduciendo el cambio de contexto de subprocesos

  • Con una alta concurrencia y un tiempo de ejecución empresarial prolongado, la clave para resolver este tipo de tarea no es el grupo de subprocesos, sino el diseño general de la arquitectura. Ver si ciertos datos en estas empresas se pueden almacenar en caché es el primer paso, y agregar servidores es el segundo paso. En cuanto a la configuración del grupo de subprocesos, consulte (2) para configurar. Por último, es posible que también sea necesario analizar el problema del largo tiempo de ejecución empresarial para ver si el middleware se puede utilizar para dividir y desacoplar tareas.

Fin de frase

No puedo garantizar que todo lo que escribo sea correcto, pero al menos puedo garantizar que no se copiará ni pegará, y que cada oración y cada línea de código se han considerado y considerado cuidadosamente. Detrás de cada artículo, espero ver mi actitud hacia la tecnología y la vida.

Además, si desea saber más sobre entrevistas multiproceso u otros puntos de conocimiento relacionados y preguntas de la entrevista, puede hacer clic en el grupo 1149778920 para unirse a nosotros y recopilarlo usted mismo, código: qf, también recopilé más de 20 años de entrevistas de empresas. Se han resuelto los puntos de conocimiento y varios puntos técnicos. Se espera que algunas capturas de pantalla a continuación sean útiles para todos.

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/w1103576/article/details/109100259
Recomendado
Clasificación