¿Qué es Java múltiple litigante?
Java proporciona un mecanismo para manejar múltiples tareas simultáneamente (simultáneamente e independientemente). Múltiples hilos coexisten en el mismo proceso JVM, por lo que comparten el mismo espacio de memoria. En comparación con múltiples procesos, la comunicación entre múltiples hilos es más ligero. En mi entendimiento, Java Multithreading es completamente para mejorar la utilización de la CPU. Los hilos de Java tienen 4 estados: nuevos, ejecutables, bloqueados y muertos. La clave está bloqueando. Bloquear significa esperar. El hilo de bloqueo no participa en la asignación de la porción de tiempo del despachador de subprocesos, por lo que naturalmente no usará la CPU. En un entorno multiproceso, esos hilos no bloqueados se ejecutan y hacen uso completo de la CPU.
Un resumen de 40 preguntas
1. ¿De qué sirve el uso de múltiples subprocesos?
Una pregunta que puede parecer sin sentido para muchas personas: puedo usar múltiples subprocesos, pero ¿de qué sirve? En mi opinión, esta respuesta es aún más tonta. El llamado "saber lo que es verdad" es "saber qué es verdad", "saber qué es verdad", "por qué usar" es "saber qué es verdad". Solo al alcanzar el nivel de "saber qué es verdad" se puede decir que puede aplicar un punto de conocimiento libremente. Ok, déjame decirte lo que pienso sobre este problema:
(1) Dé en juego completo a las ventajas de la CPU de múltiples núcleos
Con el avance de la industria, las computadoras portátiles, las computadoras de escritorio e incluso los servidores de aplicaciones comerciales son todos de doble núcleo, y no son infrecuentes de 4 núcleos, 8 núcleos o incluso 16 núcleos. Si se trata de un programa único, el 50% se desperdicia en la CPU de doble núcleo y el 75% se desperdicia en la CPU de 4 núcleos. La llamada "subprocesos múltiples" en una CPU de un solo núcleo es falso múltiple. El procesador solo procesará un pedazo de lógica al mismo tiempo, pero los hilos cambian relativamente rápido, que parece que se ejecutan múltiples hilos "al mismo tiempo". La lectura múltiple en las CPU de múltiples núcleos es el verdadero Multithreading. Puede permitir que su lógica de segmento múltiple funcione al mismo tiempo y al múltiples subprocesos. Realmente puede dar un juego completo a las ventajas de las CPU de múltiples núcleos para lograr el objetivo de hacer un uso completo de la CPU.
(2) Evitar el bloqueo
Desde la perspectiva de la eficiencia de la operación del programa, una CPU de un solo núcleo no solo no dará juego completo a las ventajas de los subprocesos múltiples, sino que reducirá la eficiencia general del programa porque ejecutar múltiples subprocesos en una CPU de un solo núcleo causará el cambio de contexto de subprocesos, lo que reducirá la eficiencia general del programa. Sin embargo, todavía necesitamos aplicar CPU múltiple a CPU de un solo núcleo para evitar el bloqueo. Imagínense, si una CPU de un solo núcleo usa un solo hilo, siempre que el hilo esté bloqueado, por ejemplo, leyendo de forma remota un cierto datos, el par no ha regresado durante mucho tiempo y no ha establecido un tiempo de tiempo de espera, entonces su programa completo dejará de ejecutarse antes de que los datos se devuelan. Múltiple subproceso puede evitar este problema. Múltiples hilos se ejecutan al mismo tiempo. Incluso si el código de un hilo se bloquea a partir de datos de lectura, no afectará la ejecución de otras tareas.
(3) Fácil de modelar
Esta es otra ventaja que no es tan obvia. Supongamos que hay una gran tarea A, programación de un solo subproceso, entonces debe considerar mucho, y es más problemático construir todo el modelo de programa. Sin embargo, si divide esta gran tarea A en varias tareas pequeñas, la Tarea B, la Tarea C y la Tarea D, crea un modelo de programa por separado y ejecuta estas tareas por separado a través de múltiples hilos, será mucho más simple.
2. Cómo crear hilos
Un problema más común es generalmente dos:
(1) heredar la clase de hilo
(2) Implementar la interfaz ejecutable
En cuanto a cuál es mejor, no hace falta decir que este último es definitivamente mejor, porque la forma de implementar interfaces es más flexible que el método de la clase de herencia, y también puede reducir el acoplamiento entre programas. La programación orientada a la interfaz también es el núcleo de los seis principios principales de los patrones de diseño.
3. La diferencia entre el método start () y run ()
Solo cuando se llame el método Start () se mostrarán las características de múltiples subprocesos, y el código en el método run () de diferentes hilos se ejecutará alternativamente. Si solo llama al método run (), el código se ejecutará sincrónicamente. Debe esperar a que se ejecute el código en el método run () de un hilo antes de que otro hilo pueda ejecutar el código en su método run ().
4. La diferencia entre la interfaz runnable y la interfaz invocable
Hay una pregunta un poco profunda, y también muestra la amplitud del conocimiento aprendido por un programador de Java.
El valor de retorno del método run () en la interfaz ejecutable es nula, y lo que hace es solo ejecutar el código en el método run (); El método Call () en la interfaz Callable tiene un valor de retorno, que es genérico, y puede usarse para obtener los resultados de la ejecución asincrónica junto con Future y FuturetAk.
En realidad, esta es una característica muy útil, porque una razón importante por la cual la lectura múltiple es más difícil y más compleja que el enhebrado único es que la lectura múltiple está llena de incógnitas. ¿Se ha ejecutado un cierto hilo? ¿Cuánto tiempo se ha ejecutado un hilo? ¿Son los datos que esperamos cuando se ejecuta un hilo? No es consciente de que todo lo que podemos hacer es esperar a que se ejecute esta tarea de múltiples subprocesos. Callable+Future/FuturetAk puede obtener los resultados de la ejecución multiproceso y puede cancelar la tarea del hilo cuando el tiempo de espera es demasiado largo y los datos requeridos no se obtienen. Es realmente útil.
5. La diferencia entre CyclicBarrier y CountdownLatch
Ambas clases que se ven un poco similares pueden usarse para indicar que el código se ejecuta en cierto punto en java.util.concurrent. La diferencia entre los dos es:
(1) Después de que un hilo de CyclicBarrier se ejecute en cierto punto, el hilo deja de funcionar. Hasta que todos los hilos lleguen a este punto, todos los hilos no volverán a funcionar; CountdownLatch no lo es. Después de que un hilo se ejecuta en cierto punto, solo da un cierto valor -1, y el hilo continúa funcionando.
(2) CyclicBarrier solo puede evocar una tarea, CountdownLatch puede evocar múltiples tareas
(3) Se puede reutilizar CyclicBarrier, CountdownLatch no se puede reutilizar, y el valor de conteo es 0 no se reutilizará, y el CountdownLatch no se volverá a utilizar nuevamente
6. El papel de las palabras clave volátiles
Un tema muy importante es que cada programador de Java que aprende y aplica múltiples subprocesos debe dominarlo. El requisito previo para comprender el papel de la palabra clave volátil es comprender el modelo de memoria Java. No hablaré sobre el modelo de memoria Java aquí. Puede consultar el punto 31. Hay dos funciones principales de la palabra clave volátil:
(1) La lectura múltiple gira principalmente en torno a las dos características de visibilidad y atomicidad. Las variables modificadas por la palabra clave volátil aseguran su visibilidad entre múltiples hilos, es decir, cada vez que se lee la variable volátil, deben ser los últimos datos.
(2) La ejecución del código subyacente no es tan simple como el lenguaje de alto nivel que vemos: programas Java. Su ejecución es el código Java -> bytecode -> Ejecutar el código C/C ++ correspondiente de acuerdo con el código ByTecode -> C/C ++ se compila en el lenguaje de ensamblaje -> Interactuar con el circuito de hardware. En realidad, para obtener un mejor rendimiento, el JVM puede reordenar las instrucciones, y pueden surgir algunos problemas inesperados bajo subprocesos múltiples. El uso de Volátil prohibirá la reordenamiento semántico, lo que, por supuesto, reduce la eficiencia de la ejecución del código hasta cierto punto
Desde un punto de vista práctico, un papel importante del volátil es combinar con CAS para garantizar la atomicidad. Para más detalles, puede consultar las clases bajo el paquete java.util.concurrent.atomic, como AtomicInteger.
7. ¿Qué es la seguridad del hilo?
Otra pregunta teórica, hay muchas respuestas. Me gustaría darle a uno que personalmente creo que sea la mejor explicación: si su código siempre puede obtener el mismo resultado cuando se ejecuta en múltiples lectura y un solo hilo, entonces su código es seguro de hilo.
Hay algo que vale la pena mencionar sobre este problema, es decir, hay varios niveles de seguridad de hilos:
(1) Inmutable
Como cadena, entero, largo, etc., son todos tipos finales. Ningún hilo puede cambiar sus valores. Si desea cambiarlos, no creará uno nuevo. Por lo tanto, estos objetos inmutables se pueden usar directamente en un entorno múltiple sin ningún medio de sincronización.
(2) Seguridad del hilo absoluto
Independientemente del entorno de tiempo de ejecución, la persona que llama no necesita medidas de sincronización adicionales. Para hacer esto, generalmente tiene que pagar muchos costos adicionales. En Java, en realidad no son clases seguras de hilo. Sin embargo, también hay clases que son absolutamente seguras de hilo, como CopyOnWriteArrayList y CopyOnWriteAryset
(3) Seguridad relativa de hilo
La seguridad relativa del hilo es lo que solemos llamar la seguridad del hilo. Por ejemplo, los métodos de Vector, Agregar y eliminar son operaciones atómicas y no se interrumpirán, pero se limita a esto. Si un hilo que atraviesa un cierto vector, se agrega un hilo al mismo tiempo, se producirá una concepción de modificación concurrente en el 99% de los casos, que es el mecanismo de fallas.
(4) Los hilos no son seguros
No hay nada que decir sobre esto. ArrayList, LinkedList, Hashmap, etc. son todas las clases no threaded.
8. Cómo obtener el archivo de volcado de subprocesos en Java
Para problemas como el bucle muerto, el punto muerto, el bloqueo, la apertura de página lenta, etc., golpear el volcado de hilo es la mejor manera de resolver el problema. El llamado volcado de hilo es la pila de hilo. Hay dos pasos para obtener la pila de subprocesos:
(1) Obtenga el PID del hilo, puede usar el comando JPS y también puede usar PS -EF | Grep Java en el entorno Linux
(2) Imprima la pila de subprocesos, puede usar el comando jstack pid y también puede usar kill -3 pid en el entorno de Linux
Además, la clase de hilo proporciona un método getStackTrace () que también se puede usar para obtener la pila de subprocesos. Este es un método de instancia, por lo que este método está vinculado a una instancia de subproceso específica. Cada vez que se ejecuta la pila actualmente en un hilo específico.
9. ¿Qué sucede si un hilo tiene una excepción de tiempo de ejecución?
Si esta excepción no se atrapa, el hilo dejará de ejecutarse. Otro punto importante es: si este hilo contiene un monitor de un determinado objeto, el monitor de objeto se liberará inmediatamente
10. Cómo compartir datos entre dos hilos
Simplemente comparta objetos entre hilos, y luego evoque y espere a través de Wait/Notify/Notifyall, espera/Signal/Signalall. Por ejemplo, el bloqueo de la cola Bloquingqueue está diseñado para compartir datos entre hilos.
11. ¿Cuál es la diferencia entre el método de sueño y el método de espera?
Con frecuencia se hace esta pregunta, tanto el método de sueño como el método de espera se pueden usar para renunciar a la CPU durante un cierto período de tiempo. La diferencia es que si el hilo contiene el monitor de un objeto, el método de sueño no renunciará al monitor de este objeto, y el método de espera renunciará al monitor de este objeto.
12. ¿Cuál es el papel del modelo de consumidor productor?
Esta pregunta es teórica, pero importante:
(1) Mejore la eficiencia operativa de todo el sistema al equilibrar la capacidad de producción de la capacidad de consumo de productores y consumidores, que es el papel más importante del modelo de consumidor de productores.
(2) Desacoplamiento, esta es la función acompañada del modelo de productor y consumidor. El desacoplamiento significa que hay menos conexiones entre productores y consumidores. Cuanto menos conexiones, más se pueden desarrollar por su cuenta sin recibir restricciones mutuas.
13. ¿De qué es el uso de ThreadLocal
En pocas palabras, ThreadLocal es una práctica de intercambiar espacio por el tiempo. Cada hilo mantiene un ThreadLocal.ThreadLocalMap implementado por el método de dirección abierta para aislar los datos y no compartir los datos, por lo que naturalmente no habrá problemas de seguridad de subprocesos.
14. ¿Por qué espera () y notificar ()/notificar los métodos
Esto es forzado por JDK. El método Wait () y notificar ()/notifyall () primero debe obtener el bloqueo del objeto antes de llamar
15. ¿Cuál es la diferencia entre el método Wait () y notificar ()/notifyall () al abandonar el monitor de objeto
La diferencia entre el método Wait () y el método notify ()/notifyall () al renunciar al monitor de objeto es que el método Wait () libera inmediatamente el monitor de objeto, mientras que el método notify ()/notifyall () esperará que el código restante del subproceso se ejecute antes de renunciar al monitor del objeto.
16. ¿Por qué usar la piscina de hilos
Evite la creación frecuente y la destrucción de los hilos para lograr la reutilización de objetos de hilo. Además, el uso de grupos de subprocesos también puede controlar de manera flexible el número de concurrencia según el proyecto.
17. Cómo detectar si un hilo contiene un monitor de objeto
También vi una pregunta de entrevista múltiple en Internet para saber que hay una manera de determinar si un subproceso contiene un monitor de objeto: la clase de subprocesos proporciona un método HoldSlock (Obj OBJ), que devolverá verdadero si y solo si el monitor del objeto OBJ está sostenido por un hilo. Tenga en cuenta que este es un método estático, lo que significa que "un cierto hilo" se refiere al hilo actual.
18. La diferencia entre sincronizado y reentrantlock
Sincronizado es la misma palabra clave que, de lo contrario, para, y mientras, y el 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, que pueden heredarse, tener métodos y tener varias variables de clase. La escalabilidad de Reentrantlock que sincronizada se refleja en varios puntos:
(1) Reentrantlock puede establecer el tiempo de espera para adquirir el bloqueo, evitando así un punto muerto
(2) Reentrantlock puede obtener varias información de bloqueo
(3) Reentrantlock puede implementar de manera flexible la notificación multicanal
Además, los mecanismos de bloqueo de los dos son realmente diferentes. El reentrantlock subyacente llama a un método de parque de inseguridad para bloquear, y la operación sincronizada debe ser la palabra marca en el encabezado del objeto, no puedo estar seguro de esto.
19. ¿Cuál es la concurrencia de concurrenthashmap?
La concurrencia de concurrenthashmap es el tamaño del segmento, que es 16 por defecto, lo que significa que en la mayoría de los 16 hilos pueden operar concurrenthashmap al mismo tiempo. Esta es también la mayor ventaja de concurrenthashmap en hashtable. En cualquier caso, ¿puede hashtable tener dos hilos al mismo tiempo obtener los datos en hashtable?
20. ¿Qué es ReadWriteLock?
En primer lugar, dejemos en claro que no es que Reentrantlock no sea bueno, es solo que Reentrantlock está limitado en algunos momentos. Si se usa ReentrantLock, puede ser evitar las inconsistencias de datos causadas por el hilo A de la escritura de datos y datos de lectura de subprocesos B. Sin embargo, de esta manera, si el hilo C leyendo datos y el hilo D también está leyendo datos, la lectura de datos no cambiará los datos. No hay necesidad de bloquearlo, pero aún lo bloquea, lo que reduce el rendimiento del programa.
Debido a esto, nació el bloqueo de lectura de lectura y redacción. ReadWriteLock es una interfaz de bloqueo de lectura-escritura. ReentRantReadWriteLock es una implementación concreta de la interfaz ReadWriteLock, que se da cuenta de la separación de lectura y escritura. Las cerraduras de lectura son compartidas y los bloqueos de escritura son exclusivos. Leer, leer y leer no será mutuamente excluyente. Solo leer y escribir, escribir y leer, escribir y escribir será mutuamente excluyente, mejorando el rendimiento de leer y escribir.
21. ¿Qué es Futuretask?
Esto en realidad se menciona anteriormente. FutUreTask representa una tarea de operación asincrónica. Se puede pasar una clase de implementación específica de llamabilidad a FuturetAk, que puede esperar el resultado de esta operación asincrónica para obtener, 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 ejecutable, FuturetAk también se puede colocar en el grupo de subprocesos.
22. Cómo encontrar qué hilo utiliza la CPU más larga en el entorno de Linux
Este es un problema más práctico, y creo que este problema es bastante significativo. Puedes hacer esto:
(1) Obtenga el PID, JPS o PS -EF del proyecto | Grep Java, que se ha mencionado anteriormente
(2) Top -H -P PID, el orden no se puede cambiar
Esto imprimirá el porcentaje de tiempo de CPU que toma el proyecto actual para cada hilo. Tenga en cuenta que el aquí es LWP, que es el número de subproceso del hilo nativo del sistema operativo. Mi Notebook Mountain no implementa proyectos Java en el entorno de Linux, por lo que no hay forma de tomar capturas de pantalla y demostraciones. Si la empresa está implementando un proyecto utilizando un entorno Linux, puede probarlo.
El uso de "Top -H -P PID" + "JPS PID" puede encontrar fácilmente una pila de subprocesos que ocupa una CPU alta, posicionando así el motivo de la ocupación de la CPU alta, que generalmente se debe a operaciones de código inadecuado que conducen a un circuito muerto.
Finalmente, permítanme mencionar que el LWP jugó con "Top -H -P PID" es decimal, y el número de hilo local jugado con "JPS PID" es hexadecimal. Después de la conversión, puede localizar la pila de hilo actual que ocupa una CPU alta.
23. Programación de Java Escriba un programa que causará un punto muerto
Vi esta pregunta por primera vez y pensé que era una muy buena pregunta. Muchas personas saben cómo es el punto muerto: el hilo A y el hilo B están esperando que las cerraduras del otro causen un bucle infinito muerto para continuar el programa. Por supuesto, solo se limita a esto. Si pregunta cómo escribir un programa de punto muerto, no lo sabrá. Para decirlo sin rodeos, no entiendes qué es un punto muerto. Si entiendes una teoría, lo harás. Básicamente no puedes ver el problema del punto muerto en la práctica.
Para comprender realmente qué es un punto muerto, esta pregunta no es difícil, hay algunos pasos:
(1) Dos hilos sostienen dos objetos de objetos: Lock1 y Lock2 respectivamente. Estas dos cerraduras sirven como cerraduras para bloques de código síncronos;
(2) En el método run () de hilo 1, el bloque de código de sincronización primero obtiene el bloqueo de objeto de Lock1, hilo. Esto se realiza principalmente para evitar que el hilo 1 obtenga continuamente bloqueos de objetos de dos objetos: bloqueo1 y bloqueo2.
(3) Ejecutar el subproceso 2) (en el método, el bloque de código de sincronización obtiene primero el bloqueo de objeto2, y luego adquiere el bloqueo del objeto1. Por supuesto, el bloqueo del objeto ya está sostenido por el bloqueo de subproceso 1, y el subproceso 2 debe esperar el hilo 1 para liberar el bloqueo de objeto Lock1.
De esta manera, después del hilo 1 "duerme" y el hilo 2 ha adquirido el bloqueo de objeto2. El hilo 1 intenta adquirir el objeto Lock2 en este momento y está bloqueado. En este momento, se forma un punto muerto. Ya no escribiré el código, ocupa mucho espacio. Java Multithreading 7: Deadlock Este artículo contiene la implementación del código de los pasos anteriores.
24. Cómo despertar un hilo de bloqueo
Si el hilo bloquea los métodos Wait (), Sleep () o Join (), puede interrumpir el hilo y despertarlo lanzando una Excepción InterruptedException; Si el hilo encuentra el bloqueo de IO, es impotente porque el sistema operativo implementa IO, y el código Java no puede contactar directamente con el sistema operativo.
25. ¿Qué ayuda ayuda a los objetos inmutables?
Un problema mencionado anteriormente es que los objetos inmutables aseguran la visibilidad de la memoria de los objetos, y no hay necesidad de sincronización adicional para leer objetos inmutables, lo que mejora la eficiencia de la ejecución del código.
26. ¿Qué es el cambio de contexto multiproceso?
La conmutación de contexto multiproceso se refiere al proceso de conmutación de control de la CPU de un hilo en ejecución a otro hilo listo y esperando que se obtengan los derechos de ejecución de la CPU.
27. Si la cola de grupo de hilos está llena cuando envía una tarea, lo que sucederá en este momento
Si está utilizando LinkedBlokingqueue, es decir, colas ilimitadas, no importa. Continúe agregando tareas a la cola de bloqueo y espere la ejecución, porque Linked Bloquingqueue casi puede considerarse una cola infinita y puede almacenar tareas infinitamente; Si está utilizando una cola limitada, por ejemplo, ArrayBlockingqueue, la tarea se agregará primero a ArrayBlockingqueue. Si el arrayblockingqueue está lleno, el rechazo de la mano de vida utilizará la política de rechazo para manejar las tareas completas, y el valor predeterminado es abortpolicy.
28. ¿Cuál es el algoritmo de programación de hilo utilizado en Java?
Estilo preventivo. Después de que un hilo use UP CPU, el sistema operativo calculará una prioridad total basada en datos como prioridad de subprocesos, hambre de subprocesos, etc. y asignará la próxima vez por corte a un hilo para su ejecución.
29. ¿Cuál es la función de Thread.sleep (0)
Esta pregunta está relacionada con la pregunta anterior, y estoy todos juntos. Dado que Java utiliza un algoritmo de programación de hilos preventivos, puede ocurrir que un hilo a menudo obtiene el control de la CPU. Para permitir que algunos hilos con una prioridad relativamente baja obtengan el control de la CPU, STIRD.Sleep (0) se puede utilizar para activar manualmente las cortes de tiempo del sistema operativo, que también es una operación para equilibrar el control de la CPU.
30. ¿Qué es el giro?
Muchos códigos sincronizados son solo un código muy simple, y el tiempo de ejecución es muy rápido. Bloquear los hilos que esperan en este momento puede ser una operación que no valga la pena, porque el bloqueo de subprocesos implica conmutación de estado de usuario y estado de núcleo. Dado que el código sincronizado se ejecuta muy rápido, también podría dejar que el hilo que espere que el bloqueo no se bloquee, sino que realice bucles ocupados en el límite de sincronizado. Esto es giro. Si ha realizado múltiples bucles ocupados y descubre que el bloqueo no se ha obtenido, y luego bloquearlo, esta puede ser una mejor estrategia.
31. ¿Qué es el modelo de memoria de Java?
El modelo de memoria Java define una especificación para el acceso múltiple a la memoria Java. El modelo de memoria Java debe explicarse completamente, pero no puedo explicarlo claramente en unas pocas oraciones aquí. Déjame resumirlo brevemente.
Varias partes del modelo de memoria Java:
(1) El modelo de memoria Java divide la memoria en la memoria principal y la memoria de trabajo. El estado de la clase, es decir, las variables compartidas entre clases, se almacenan 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 y les permitirá existir en su propia memoria de trabajo. Al ejecutar su propio código de subproceso, utiliza estas variables y opera la que está en su propia memoria de trabajo. Después de ejecutar el código de subproceso, el último valor se actualizará a la memoria principal.
(2) Varias operaciones atómicas se definen para operar variables en la memoria principal y la memoria de trabajo
(3) Defina las reglas para usar variables volátiles
(4) sucede antes, es decir, el principio de la primera ocurrencia, define algunas reglas en las que la operación A debe ocurrir primero en la operación B. Por ejemplo, el código delante del flujo de control en el mismo hilo debe ocurrir primero en el código detrás del flujo de control, la acción de liberar el desbloqueo de bloqueo debe ocurrir primero en la acción de bloquear el mismo bloqueo, etc., siempre que estas reglas se meten, no se requieran medidas de sinquonaciones adicionales. Si una determinada pieza de código no cumple con todas las reglas de antes, entonces este código debe ser seguro.
32. ¿Qué es CAS?
CAS, Nombre completo comparado y establecido, se compara. Supongamos que hay tres operandos: el valor de memoria V, el valor esperado anterior A, el valor B para ser modificado. Si y solo si el valor esperado A y el valor de memoria V son el mismo, el valor de memoria se modificará a B y se devuelve verdadero, de lo contrario, no se hará nada y se devolverá falso. Por supuesto, CAS debe cooperar con la variable volátil, para asegurarse de 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 A que no cambiará para un hilo. Mientras una determinada operación CAS falle, nunca tendrá éxito.
33. ¿Qué es el bloqueo optimista y el bloqueo pesimista?
(1) Bloqueo optimista: al igual que su nombre, es optimista sobre los problemas de seguridad del hilo causados por operaciones concurrentes. Optimistic Lock cree que la competencia no siempre ocurre, por lo que no necesita sostener el bloqueo y se comparará: establecer estas dos acciones como una operación atómica para tratar de modificar las variables en la memoria. Si falla, significa que ocurre un conflicto, y entonces debería haber una lógica de reintento correspondiente.
(2) Bloqueo pesimista: al igual que su nombre, es pesimista sobre los problemas de seguridad del hilo causados por operaciones concurrentes. Las cerraduras pesimistas creen que la competencia siempre ocurrirá. Por lo tanto, cada vez que se opera un recurso, mantendrá un bloqueo exclusivo, al igual que sincronizado, independientemente de si está bloqueado directamente, y el recurso será operado.
34. ¿Qué es AQS
Hablemos brevemente de AQS. El nombre completo de AQS es Abstractqueed Psychronizer. Debe ser un sincronizador de cola abstracto cuando se traduce.
Si la base de java.util.concurrent es CAS, entonces AQS es el núcleo de todo el paquete de concurrencia de Java, y reentrantLock, CountdownLatch, Semaphore, etc. Todos lo usan. AQS en realidad conecta toda la entrada en forma de una cola bidireccional. Por ejemplo, Reentrantlock. Todos los hilos de espera se colocan en una entrada y se conectan a una cola bidireccional. Si el hilo anterior usa ReentrantLock, entonces la primera entrada de la cola bidireccional en realidad comienza a funcionar.
AQS define todas las operaciones en colas bidireccionales, pero solo abre los métodos Trylock y Tryrelasege para que los desarrolladores los usen. Los desarrolladores pueden reescribir los métodos de trylock e tryrelaseed de acuerdo con su propia implementación para implementar sus propias funciones de concurrencia.
35. SEGURIDAD DEL PRUEBA DEL MODO SINGLETON
Un problema de cliché, lo primero que debe decir es que la seguridad del hilo del patrón Singleton significa que: las instancias de una determinada clase solo se crearán una vez en un entorno múltiple. Hay muchas maneras de escribir un patrón de singleton, déjame resumir:
(1) Escribir el patrón Singleton de Hungry Man: Hilt Safety
(2) Escribir el patrón de singleton perezoso: no seguro
(3) Escriba el modo singleton de bloqueo de doble verificación: Seguridad de subprocesos
36. ¿Cuál es la función del semáforo?
Semaphore es un semáforo, y su función es limitar el número de concurrencia en un determinado bloque de código. Semaphore tiene un constructor que puede pasar int Integer n, lo que indica que solo los n hilos pueden acceder a una determinada pieza de código. Si se excede N, espere hasta que un hilo complete el bloque de código e ingrese el siguiente hilo. A partir de esto, podemos ver que si el Integer int Integer pase en el constructor semáforo es equivalente a convertirse en sincronizado.
37. Solo hay una declaración "Recuento de retorno" en el método size () de hashtable, entonces, ¿por qué aún necesita sincronizar?
Esta es una confusión que tenía antes, y me pregunto si has pensado en esta pregunta. Si hay múltiples declaraciones en un método y todas operan la misma variable de clase, entonces si no agrega bloqueos en un entorno múltiple, inevitablemente causará problemas de seguridad de subprocesos. Esto es fácil de entender, pero el método size () claramente tiene solo una declaración, entonces, ¿por qué aún necesita agregar bloqueos?
Con respecto a este problema, lo he entendido trabajando y estudiando lentamente, y hay dos razones principales:
(1) Solo un hilo puede ejecutar el método de sincronización de la clase fija al mismo tiempo, pero para el método asincrónico de la clase, múltiples hilos pueden acceder a él al mismo tiempo. Entonces, hay un problema. Tal vez Thread A está agregando datos al ejecutar el método PUT de HashTable, y Thread B puede llamar al método size () normalmente para leer el número de elementos actuales en la HAESTABLE. El valor leído puede no ser el último. Tal vez Thread A ha agregado los datos, pero sin tamaño ++, el subproceso B ya ha leído el tamaño. Entonces, para el hilo B, el tamaño de lectura 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 la subproceso, llama al método de poner, lo que garantiza la seguridad de los subprocesos
(2) La CPU ejecuta código, pero no es código Java. Esto es muy importante y debes recordarlo. El código Java finalmente se traduce al código de ensamblaje para la ejecución, y el código de ensamblaje 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 el código Java se compila, solo hay una línea de bytecodo generada, no significa que para la capa subyacente, solo hay una operación para esta declaración. El "recuento de retorno" de la oración supone que se traduce en tres declaraciones de ensamblaje para ejecutar, y es completamente posible que el hilo cambie después de ejecutar la primera oración.
38. ¿Qué hilo es el constructor de la clase de hilo y el bloque estático llamado por qué hilo
Esta es una pregunta muy complicada y astuta. Recuerde: el constructor y el bloque estático de la clase de subprocesos se llaman por el hilo donde se encuentra la nueva clase de subprocesos, y el hilo llama el código en el método Ejecutar.
Si la instrucción anterior lo confunde, entonces déjame darte un ejemplo, supongamos que el nuevo Thread1 está en Thread2 y el nuevo Thread2 está en la función principal, entonces:
(1) El constructor de Thread2 y el bloque estático se llaman por el hilo principal, y el método run () de Thread2 se llama por Thread2 mismo
(2) El constructor de Thread1 y el bloque estático se llaman por Thread2, y el método Run () de Thread1 se llama por Thread1 mismo
39. ¿Cuál es la mejor opción entre el método de sincronización y el bloque de sincronización?
Bloques de sincronización, lo que significa que el código fuera del bloque de sincronización se ejecuta asincrónicamente, lo que mejora la eficiencia del código más eficiente que sincronizar todo el método. Conozca un principio: el rango de sincronización menos
Cuanto mejor.
Con este artículo, me gustaría mencionar que, aunque cuanto más pequeño sea el rango de sincronización, mejor, todavía hay un método de optimización llamado grosería en las máquinas virtuales Java, que es aumentar el rango de sincronización. Esto es útil. Por ejemplo, StringBuffer es una clase segura de hilo. Naturalmente, el método append () más utilizado es un método de sincronización. Cuando escribimos código, agregaremos repetidamente la cadena, lo que significa bloqueo repetidamente -> desbloqueo, lo que no es bueno para el rendimiento, porque significa que la máquina virtual Java tiene que cambiar repetidamente entre el estado del núcleo y el estado de usuario en este hilo. Por lo tanto, la máquina virtual Java realiza una operación de bloqueo de bloqueo en el código llamado por el método de apertura múltiple, extendiendo múltiples operaciones de apertura a la cabeza y la cola del método de apertura, y convirtiéndolo en un gran bloque de sincronización. Esto reduce el número de bloqueos de bloqueo-> tiempos de desbloqueo, mejorando 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 cortas? ¿Cómo usar grupos de subprocesos para empresas con baja concurrencia y tiempo de ejecución de tareas largas? ¿Cómo usar grupos de subprocesos para empresas con alta concurrencia y largo tiempo de ejecución de servicios?
Esta es una pregunta que vi en el sitio web de programación concurrente. Puse esta pregunta al final y espero que todos puedan ver y pensar en ello, porque esta pregunta es muy buena, muy práctica y muy profesional. Con respecto a este tema, mi opinión personal es:
(1) Alta concurrencia y tiempo de ejecución de tareas cortas, el número de subprocesos de grupo de subprocesos se puede establecer en el número de núcleo de la CPU +1 para reducir la conmutación de contexto de subprocesos
(2)并发不高、任务执行时间长的业务要区分开看:
a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务
b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换
(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
Java线程阻塞(Blocked)的类型:
调用sleep函数进入睡眠状态,Thread.sleep(1000)或者TimeUnit.SECONDS.sleep(1),sleep不会释放锁。
等待(wait)某个事件,分为两种,(wait,notify,notifyAll),(await, signal,signalAll) ,后面会详细介绍。wait和await会释放锁,且必须在获取到锁的环境才能调用。
等待锁,synchronized和lock环境中,锁已经被别的线程拿走,等待获取锁。
IO阻塞(Blocked),比如网络等待,文件打开,控制台读取。System.in.read()。