texto
Al programar en un entorno concurrente, se requiere un mecanismo de bloqueo para sincronizar las operaciones entre múltiples hilos para garantizar un acceso mutuamente excluyente a los recursos compartidos. El bloqueo puede causar daño a rendimiento, lo que parece ser bien conocido. Sin embargo, el bloqueo en sí mismo no trae mucho consumo de rendimiento, y el rendimiento es principalmente el proceso de adquisición de bloqueos en los subprocesos. Si solo hay un hilo que compite por cerraduras y no hay competencia de múltiples hilos en este momento, entonces el JVM optimizará y el consumo de rendimiento causado por el bloqueo se puede ignorar básicamente. Por lo tanto, estandarizar el funcionamiento del bloqueo, optimizar el método de uso de bloqueo y evitar la competencia innecesaria de los hilos, no solo puede mejorar el rendimiento del programa, sino también evitar la posibilidad de matar el punto muerto de hilo causado por el bloqueo irregular y mejorar la robustez del programa. Lo siguiente explica varias ideas de optimización de bloqueos.
1. Intenta no bloquear el método
Cuando se agrega un bloqueo a una función de miembro normal, el hilo obtiene el bloqueo del objeto del objeto donde se encuentra el método. En este momento, todo el objeto se bloqueará. Esto también significa que si los métodos de sincronización múltiple proporcionados por este objeto son para diferentes servicios, entonces, dado que todo el objeto está bloqueado, cuando se procesa una empresa, otros hilos comerciales no relacionados también deben esperar. El siguiente ejemplo muestra esto:
La clase LockMethod contiene dos métodos de sincronización, que se llaman en dos procesos comerciales:
public class LockMethod {public sincronizado vacío busia () {for (int i = 0; i <10000; i ++) {System.out.println (Thread.CurrentThread (). GetName ()+"Tratar con el negocio A:"+i); }} public sincronizado vacío busib () {for (int i = 0; i <10000; i ++) {System.out.println (Thread.CurrentThread (). getName ()+"Trata con el negocio b:"+i); }}}Bussa es una clase de subprocesos utilizada para manejar un negocio y llama al método Busia () de LockMethod:
Public Class Bussb extiende el hilo {LockMethod LockMethod; trato vacío (LockMethod LockMethod) {this.lockmethod = LockMethod; } @Override public void run () {super.run (); LockMethod.busib (); }}La clase de TestlockMethod utiliza Thread Bussa y Bussb para el procesamiento de negocios:
Public Class TestlockMethod extiende el hilo {public static void main (String [] args) {LockMethod LockMethod = new LockMethod (); Bussa bussa = new Bussa (); Bussb bussb = new Bussb (); bussa.deal (Lockmethod); bussb.deal (LockMethod); bussa.start (); bussb.start (); }}Al ejecutar el programa, puede ver que durante la ejecución de Thread Bussa, BussB no puede ingresar a la función Bussib (), porque el bloqueo del objeto Lockmethod se obtiene por el subestino bussa.
2. Reduzca el bloque de código sincrónico y bloquee solo los datos
A veces para la conveniencia de programación, algunas personas sincronizaron un gran código. Si algunas operaciones en este bloque de código no están relacionadas con los recursos compartidos, deben colocarse fuera del bloque sincrónico para evitar sostener cerraduras durante mucho tiempo, lo que hace que otros hilos permanezcan en un estado de espera. Especialmente algunas operaciones de ciclo y operaciones de E/S sincrónicas. No solo significa reducir el bloque de sincronización en el rango de línea del código, sino también en la lógica de ejecución, el bloque de sincronización debe reducirse. Por ejemplo, agregue más juicios condicionales y sincronice si cumplen con las condiciones, en lugar de realizar juicios condicionales después de la sincronización, para minimizar la lógica innecesaria que ingresa al bloque de sincronización.
3. Trate de no incluir cerraduras en la cerradura
Esta situación a menudo ocurre. Después de que el hilo obtiene el bloqueo A, llama al método de sincronización de otro objeto en el bloque del método de sincronización y obtiene el segundo bloqueo. Esto puede conducir a múltiples solicitudes de bloqueo en una pila de llamadas. En el caso de múltiples subprocesos, puede causar excepciones muy complejas y difíciles de analizar, lo que resulta en bloqueos muertos. El siguiente código muestra esto:
sincronizado (a) {sincronizado (b) {}}O el método de sincronización se llama en el bloque de sincronización:
sincronizado (a) {b b = objarrayList.get (0); b.method (); // Este es un método de sincronización}La solución es saltar y agregar cerraduras, y no incluye cerraduras:
{B b = nulo; sincronizado (a) {b = objarrayList.get (0); } b.method ();} 4. Privatizar el bloqueo y administrar el bloqueo internamente
Es más seguro usar el bloqueo como objeto privado, y no se puede obtener desde el exterior. Un objeto puede ser bloqueado directamente por otros hilos, y el hilo contiene el bloqueo del objeto del objeto, por ejemplo:
clase A {public void Method1 () {}} clase B {public void Method1 () {a a = new a (); sincronizado (a) {// bloquear directamente a.method1 (); }}}En esta forma de uso, el bloqueo del objeto del objeto A está sostenido por el exterior, por lo que es más peligroso dejar que el bloqueo se use en múltiples lugares fuera, y también hace que los problemas lean el flujo lógico del código. Una mejor manera es administrar las cerraduras dentro de la clase y proporcionar operaciones de sincronización a través de interfaces cuando se necesita el esquema sincrónico externo:
Clase A {bloqueo privado bloqueador = nuevo objeto (); public void Method1 () {sincronizado (bloqueo) {}}} clase B {public void Method1 () {a a = new a (); a.method1 (); }} 5. Realice una descomposición de bloqueo apropiado
Considere el siguiente procedimiento:
Public Class Gameserver {public Map <String, List <Player>> Tablas = New HashMap <String, List <Rugeer>> (); public void Join (Player Player, Table Table) {if (Player.GetAccountBalance ()> table.getLimit ()) {SynChronized (Tablas) {List <Player> tablePlayers = Tables.get (table.getID ()); if (tablePlayers.size () <9) {tablePlayers.add (jugador); }}}}} public void Leave (Player Player, Table Table) {/*omit*/} public void createTable () {/*omit*/} public void Destroytable (tabla de tabla) {/*omit*/}}En este ejemplo, el método de unión solo usa un bloqueo de sincronización para obtener el objeto de la lista <player> en las tablas, y luego determinar si el número de jugadores es inferior a 9. Si es así, agregue un jugador. Cuando hay miles de listas <Player> en tablas, la competencia por las cerraduras de las tablas será muy feroz. Aquí, podemos considerar la descomposición del bloqueo: después de obtener rápidamente los datos, bloquee el objeto de la lista <Player>, para que otros subprocesos puedan competir rápidamente para obtener el bloqueo del objeto de las tablas:
Public Class Gameserver {
mapa público <cadena,
Lista <Player>> Tablas = New HashMap <String,
Lista <Player>> ();
Public void se une (jugador jugador, mesa de mesa) {
if (jugador.getAccountBalance ()> table.getLimit ()) {
Lista <Seryer> tablePlayers = null;
sincronizado (tablas) {
tablePlayers = Tables.get (table.getId ());
}
Sincronizado (TablePlayers) {
if (tablePlayers.size () <9) {
tableplayers.add (jugador);
}
}
}
}
Public void Leave (jugador jugador, mesa de mesa) {
/*Omitido*/
}
public void createTable () {
/*Omitido*/
}
Public void Destroytable (tabla de tabla) {
/*Omitido*/
}
}