1. Escenarios aplicables para CAS y sincronizado
1. Para situaciones en las que la competencia de recursos es menor, el uso de un bloqueo de sincronización sincronizado para el bloqueo de subproces y las operaciones de conmutación y conmutación entre los núcleos de estado de usuario es un desperdicio adicional de recursos de CPU; Si bien CAS se implementa en función del hardware, no necesita ingresar al núcleo, no necesita cambiar los subprocesos, y la posibilidad de operar es menor, por lo que se puede obtener un mayor rendimiento.
2. En caso de competencia de recursos serios, la probabilidad de girar CAS es relativamente alta, desperdiciando más recursos de CPU y siendo menos eficiente que sincronizado. Tomando la clase AtomicInteger en el paquete java.util.concurrent.atomic como ejemplo, su método getandincrement () se implementa de la siguiente manera:
public Final int getAndincrement () {for (;;) {int current = get (); int next = corriente + 1; if (compareandSet (actual, siguiente)) return corriente; }}Si el método comparado (actual, siguiente) se ejecuta correctamente, se devolverá directamente; Si la competencia de hilo es feroz, lo que resulta en el método de comparación (actual, actual, siguiente) que no se puede ejecutar con éxito, se enojará y se esperará hasta que la caída de tiempo asignada por la CPU a la hilo se agote, reduciendo así en gran medida la eficiencia.
2. Escenarios de uso de errores de CAS
clase pública casdemo {private final int thread_num = 1000; Private final int max_value = 20000000; AtomicInteger privado Casi = nuevo AtomicInteger (0); privado int synci = 0; Private String Path = "/Users/Pingping/Datacenter/Books/Linux/Linux Common Commands.txt"; public void casadd () lanza interruptedException {long begin = system.currentTimemillis (); Thread [] hilos = new Thread [Thread_num]; for (int i = 0; i <thread_num; i ++) {threads [i] = new Thread (new runnable () {public void run () {while (casi.get () <max_value) {casi.getandincrement ();}}}); hilos [i] .Start (); } for (int j = 0; j <thread_num; j ++) {hilos [j] .Join (); } System.out.println ("CAS cuesta tiempo:" + (System.CurrentTimemillis () - begin)); } public void syncadd () lanza interruptedException {long begin = system.currentTimemillis (); Thread [] hilos = new Thread [Thread_num]; for (int i = 0; i <thread_num; i ++) {threads [i] = new Thread (new runnable () {public void run () {while (synci <max_value) {sincronizado ("sinci") {++ sinci;}}}}}); hilos [i] .Start (); } para (int j = 0; j <thread_num; j ++) hilos [j] .Join (); System.out.println ("Sync Costs Time:" + (System.CurrentTimemillis () - Begin)); }}Ejecutando en mi CPU de doble núcleo, el resultado es el siguiente:
Se puede ver que bajo diferentes hilos, el tiempo dedicado al cálculo de CAS es mucho más que el de sincronizado. La razón es la línea 15
14 while (casi.get () <max_value) {15 casi.getandincrement (); 16}La operación es una operación que requiere mucho tiempo. Después de ejecutar 15 líneas, el bucle se ingresará inmediatamente y la ejecución continuará, lo que dará como resultado serios conflictos de hilos.
3. Escenarios de uso de CAS mejorados
Para resolver el problema anterior, solo es necesario hacer que el tiempo de ejecución de cada bucle sea más largo, es decir, puede reducir en gran medida los conflictos de hilos. Modifique el código de la siguiente manera:
clase pública casdemo {private final int thread_num = 1000; Private final int max_value = 1000; AtomicInteger privado Casi = nuevo AtomicInteger (0); privado int synci = 0; Private String Path = "/Users/Pingping/Datacenter/Books/Linux/Linux Comandos Common Common Explation.txt"; public void casadd2 () lanza interruptedException {long begin = system.currentTimemillis (); Thread [] hilos = new Thread [Thread_num]; for (int i = 0; i <thread_num; i ++) {threads [i] = new Thread (new runnable () {public void run () {while (casi.get () <max_value) {casi.getAndIncrement (); try (inputStream in = new FileSstream (new File (Path)))) e) {E.PrintStackTrace (); hilos [i] .Start (); } para (int j = 0; j <thread_num; j ++) hilos [j] .Join (); System.out.println ("CAS Random Costs Time:" + (System.CurrentTimemillis () - Begin)); } public void syncadd2 () lanza interruptedException {long begin = system.currentTimemillis (); Thread [] hilos = new Thread [Thread_num]; for (int i = 0; i <shiff_num; i ++) {hilts [i] = new Thread (new Runnable () {public void run () {while (synci <max_value) {sincronizado ("sinci") {++ synci;} thy (inputStream in = new FileInputReam (new File (pATH)) {while (in.1); } catch (ioException e) {E.PrintStackTrace (); hilos [i] .Start (); } para (int j = 0; j <thread_num; j ++) hilos [j] .Join (); System.out.println ("Sync Costs Time:" + (System.CurrentTimemillis () - Begin)); }}En el bucle While, se agrega una operación para leer el contenido del archivo, que toma aproximadamente 40 ms, reduciendo así los conflictos de subprocesos. Los resultados de la prueba son los siguientes:
Se puede ver que cuando los conflictos de recursos son relativamente pequeños, el método CAS y la eficiencia de sincronización sincronizada son similares. ¿Por qué CAS no alcanza un mayor rendimiento que sincronizado?
El JDK utilizado en la prueba es 1.7. A partir de JDK1.6, se han introducido muchas optimizaciones en la implementación de cerraduras, como el revestimiento de bloqueos, la eliminación de bloqueos, el bloqueo liviano, el bloqueo sesgado, el hilado adaptativo y otras tecnologías para reducir la sobrecarga de la operación de bloqueo. El principio del bloqueo de giro es similar al giro de CAS, y está aún más optimizado que el giro CAS. Para más detalles, consulte el mecanismo de bloqueo JVM en profundidad 1 sincronizado.
4. Resumen
1. Cuando se usa CAS, cuando los conflictos de hilos son graves, el rendimiento del programa se reducirá considerablemente; CAS solo es adecuado para situaciones donde hay menos conflictos de hilos.
2. Sincronizado se ha mejorado y optimizado después de JDK1.6. La implementación subyacente de sincronizado se basa principalmente en la cola de Lock-Free. La idea básica es bloquear después del giro, continuar compitiendo por las cerraduras después del cambio de competencia, sacrificando ligeramente la equidad, pero obteniendo un alto rendimiento. Cuando hay menos conflictos de hilos, se puede obtener un rendimiento similar; Cuando hay serios conflictos de hilos, el rendimiento es mucho más alto que el de CAS.
El resumen anterior de la programación concurrente de Java: utilizando CAS cuidadosamente es la explicación detallada del editor. Espero que pueda darle una referencia y espero que pueda apoyar más a Wulin.com.