En Java, la palabra clave sincronizada se puede utilizar para el control de sincronización de subprocesos para lograr un acceso secuencial a los recursos clave y evitar la inconsistencia de datos causada por la ejecución concurrente de múltiples subprocesos. El principio de sincronizado es un monitor de objeto (bloqueo). Solo el hilo que adquiere el monitor puede continuar ejecutándose; de lo contrario, el hilo esperará para adquirir el monitor. Cada objeto o clase en Java tiene un bloqueo asociado con él. Para un objeto, monitorea la variable de instancia de este objeto. Para una clase, monitorea la variable de clase (una clase en sí es un objeto de la clase de clase, por lo que el bloqueo asociado con la clase también es un bloqueo de objeto). Hay dos formas de usar palabras clave sincronizadas: método sincronizado y bloque sincronizado. Ambas áreas de monitoreo están asociadas con un objeto introducido. Cuando llegue a este área de monitoreo, el JVM bloqueará el objeto de referencia, y cuando salga, se lanzará el bloqueo en el objeto de referencia (el JVM liberará el bloqueo cuando haya una salida de excepción). Los bloqueos de objetos son mecanismos internos de JVM. Solo necesita escribir métodos de sincronización o bloques de sincronización. Al operar áreas de monitoreo, el JVM adquirirá o liberará automáticamente el bloqueo.
Ejemplo 1
Primero veamos el primer ejemplo. En Java, solo hay un área crítica del mismo objeto al que se permite acceder al mismo tiempo (todos los métodos sincronizados no estáticos):
Concurrencia de paquete; clase pública Main8 {public static void main (string [] args) {cuenta cuenta = nueva cuenta (); cuenta.setBalance (1000); Compañía de la empresa = nueva empresa (cuenta); Thread CompanyThread = New Thread (Company); Banco bancario = nuevo banco (cuenta); Thread BankThread = New Thread (Bank); System.out.printf ("Cuenta: saldo inicial: %f/n", cuenta.getBalance ()); CompanyThread.start (); bankthread.start (); Pruebe el método {// Join () espera que estos dos hilos completen CompanyThread.Join (); BankThread.Join (); System.out.printf ("Cuenta: saldo final: %f/n", cuenta.getBalance ()); } catch (InterruptedException e) {E.PrintStackTrace (); }}} /*Cuenta*/cuenta de clase {saldo doble privado; /*Agregar datos entrantes para equilibrar el equilibrio*/ public sincronizado void addamount (doble cantidad) {doble tmp = balance; intente {thread.sleep (10); } catch (InterruptedException e) {E.PrintStackTrace (); } tmp += cantidad; balance = tmp; } /*Deducir datos entrantes del balance de saldo* / public sincronizado void sutractamount (cantidad doble) {doble tmp = balance; intente {thread.sleep (10); } catch (InterruptedException e) {E.PrintStackTrace (); } tmp -= cantidad; balance = tmp; } public Double GetBalance () {Balance de retorno; } public void setBalance (doble balance) {this.balance = balance; }} /*Bank*/class bank implements runnable {cuenta privada cuenta; Public Bank (cuenta de cuenta) {this.account = cuenta; } @Override public void run () {for (int i = 0; i <100; i ++) {cuenta.subtractamount (1000); }}} /*Company*/Class Company implementa Runnable {Cuenta de cuenta privada; Compañía pública (cuenta de cuenta) {this.account = cuenta; } @Override public void run () {for (int i = 0; i <100; i ++) {cuenta.Addamount (1000); }}}Ha desarrollado una aplicación de simulación para cuentas bancarias que pueden recargar y deducir saldos. Este programa recarga la cuenta llamando al método Addamount () 100 veces, depositando 1,000 cada vez; Luego deduce el saldo de la cuenta llamando al método SINTRATRAMOUNT () 100 veces, deduciendo 1,000 cada vez; Esperamos que el saldo final de la cuenta sea igual al saldo inicial, y lo implementamos a través de la palabra clave sincronizada.
Si desea ver el problema de acceso concurrente de los datos compartidos, solo necesita eliminar las palabras clave sincronizadas en las declaraciones del método addAMOUNT () y SISTATRAMOUNT (). Sin la palabra clave sincronizada, el valor de balance impreso no es consistente. Si ejecuta este programa varias veces, obtendrá diferentes resultados. Debido a que el JVM no garantiza la orden de ejecución de los subprocesos, cada vez que se ejecuta, los subprocesos leerán y modificarán el saldo de la cuenta en diferentes órdenes, lo que resulta en diferentes resultados finales.
El método de un objeto se declara utilizando la palabra clave sincronizada y solo se puede acceder con un hilo. Si el subproceso A está ejecutando un método de sincronización SyncMethoda (), Thread B quiere ejecutar otros métodos de sincronización SyncMethodb () de este objeto, el hilo B se bloqueará hasta que el hilo A complete el acceso. Pero si el hilo B accede a diferentes objetos de la misma clase, ninguno de los hilos se bloqueará.
Ejemplo 2
Demuestre el problema de que los métodos sincronizados estáticos y los métodos sincronizados no estatales en el mismo objeto pueden acceder mediante múltiples hilos al mismo tiempo. Verificarlo.
Concurrencia de paquete; clase pública Main8 {public static void main (string [] args) {cuenta cuenta = nueva cuenta (); cuenta.setBalance (1000); Compañía de la empresa = nueva empresa (cuenta); Thread CompanyThread = New Thread (Company); Banco bancario = nuevo banco (cuenta); Thread BankThread = New Thread (Bank); System.out.printf ("Cuenta: saldo inicial: %f/n", cuenta.getBalance ()); CompanyThread.start (); bankthread.start (); Pruebe el método {// Join () espera que estos dos hilos completen CompanyThread.Join (); BankThread.Join (); System.out.printf ("Cuenta: saldo final: %f/n", cuenta.getBalance ()); } catch (InterruptedException e) {E.PrintStackTrace (); }}} /*Cuenta*/cuenta de clase {/*Cámbielo a una variable estática aquí*/Private static Double Balance = 0; /*Agregar datos entrantes al saldo del saldo, tenga en cuenta que se modifica con static*/ public static sincronizado addamount (doble cantidad) {doble tmp = balance; intente {thread.sleep (10); } catch (InterruptedException e) {E.PrintStackTrace (); } tmp += cantidad; balance = tmp; } /*Deduce los datos entrantes del saldo de saldo* / public sincronizado sincronizado sustrato (cantidad doble) {doble tmp = balance; intente {thread.sleep (10); } catch (InterruptedException e) {E.PrintStackTrace (); } tmp -= cantidad; balance = tmp; } public Double GetBalance () {Balance de retorno; } public void setBalance (doble balance) {this.balance = balance; }} /*Bank*/class bank implements runnable {cuenta privada cuenta; Public Bank (cuenta de cuenta) {this.account = cuenta; } @Override public void run () {for (int i = 0; i <100; i ++) {cuenta.subtractamount (1000); }}} /*Company*/Class Company implementa Runnable {Cuenta de cuenta privada; Compañía pública (cuenta de cuenta) {this.account = cuenta; } @Override public void run () {for (int i = 0; i <100; i ++) {cuenta.Addamount (1000); }}}Acabo de agregar la palabra clave estática para modificar el saldo en el ejemplo anterior, y el método Addamount () también puede modificar la palabra clave estática. ¡Puede probar los resultados de la ejecución usted mismo, y cada ejecución tendrá un resultado diferente!
Algún resumen: