1. Applicable scenarios for CAS and synchronized
1. For situations where resource competition is less, using synchronized synchronization lock for thread blocking and wake-up switching and switching operations between user-state kernels is extra waste of CPU resources; while CAS is implemented based on hardware, does not need to enter the kernel, does not need to switch threads, and the chance of operating spin is less, so higher performance can be obtained.
2. In case of serious resource competition, the probability of CAS spin is relatively high, thus wasting more CPU resources and being less efficient than synchronized. Taking the AtomicInteger class in the java.util.concurrent.atomic package as an example, its getAndIncrement() method is implemented as follows:
public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; }}If the compareAndSet(current, next) method is successfully executed, it will be returned directly; if the thread competition is fierce, resulting in the compareAndSet(current, next) method that cannot be successfully executed, it will be looped and waited until the time slice allocated by the CPU to the thread is exhausted, thereby greatly reducing efficiency.
2. CAS error usage scenarios
public class CASDemo { private final int THREAD_NUM = 1000; private final int MAX_VALUE = 20000000; private AtomicInteger casI = new AtomicInteger(0); private int syncI = 0; private String path = "/Users/pingping/DataCenter/Books/Linux/Linux common commands.txt"; public void casAdd() throws InterruptedException { long begin = System.currentTimeMillis(); Thread[] threads = 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(); } } }); threads[i].start(); } for (int j = 0; j < THREAD_NUM; j++) { threads[j].join(); } System.out.println("CAS costs time: " + (System.currentTimeMillis() - begin)); } public void syncAdd() throws InterruptedException { long begin = System.currentTimeMillis(); Thread[] threads = 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) { synchronized ("syncI") { ++syncI; } } } } }); threads[i].start(); } for (int j = 0; j < THREAD_NUM; j++) threads[j].join(); System.out.println("sync costs time: " + (System.currentTimeMillis() - begin)); }}Running on my dual core CPU, the result is as follows:
It can be seen that under different threads, the time spent using CAS calculation is much more than that of synchronized. The reason is line 15
14 while (casI.get() < MAX_VALUE) {15 casI.getAndIncrement();16 }The operation is a very time-consuming operation. After 15 lines are executed, the loop will be immediately entered and the execution will continue, resulting in serious thread conflicts.
3. Improved CAS usage scenarios
In order to solve the above problem, it is only necessary to make the execution time of each loop longer, that is, it can greatly reduce thread conflicts. Modify the code as follows:
public class CASDemo { private final int THREAD_NUM = 1000; private final int MAX_VALUE = 1000; private AtomicInteger casI = new AtomicInteger(0); private int syncI = 0; private String path = "/Users/pingping/DataCenter/Books/Linux/Linux common commands detailed explanation.txt"; public void casAdd2() throws InterruptedException { long begin = System.currentTimeMillis(); Thread[] threads = 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 FileInputStream(new File(path))) { while (in.read() != -1); } catch (IOException e) { e.printStackTrace(); } } } } }); threads[i].start(); } for (int j = 0; j < THREAD_NUM; j++) threads[j].join(); System.out.println("CAS Random costs time: " + (System.currentTimeMillis() - begin)); } public void syncAdd2() throws InterruptedException { long begin = System.currentTimeMillis(); Thread[] threads = 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) { synchronized ("syncI") { ++syncI; } try (InputStream in = new FileInputStream(new File(path))) { while (in.read() != -1); } catch (IOException e) { e.printStackTrace(); } } } }); threads[i].start(); } for (int j = 0; j < THREAD_NUM; j++) threads[j].join(); System.out.println("sync costs time: " + (System.currentTimeMillis() - begin)); }}In the while loop, an operation to read the file contents is added, which takes about 40ms, thereby reducing thread conflicts. The test results are as follows:
It can be seen that when resource conflicts are relatively small, the CAS method and synchronized synchronization efficiency are similar. Why doesn't CAS achieve higher performance than synchronized?
The jdk used in the test is 1.7. Starting from jdk1.6, a lot of optimizations have been introduced to the implementation of locks, such as lock coarsing, lock elimination, lightweight locking, Biased Locking, Adaptive Spinning and other technologies to reduce the overhead of lock operation. The principle of spin lock is similar to CAS spin, and is even more optimized than CAS spin. For details, please refer to the in-depth JVM lock mechanism 1-synchronized.
4. Summary
1. When using CAS, when thread conflicts are serious, the program performance will be greatly reduced; CAS is only suitable for situations where there are fewer thread conflicts.
2. Synchronized has been improved and optimized after jdk1.6. The underlying implementation of synchronized mainly relies on the queue of Lock-Free. The basic idea is to block after spin, continue to compete for locks after competition switching, slightly sacrificing fairness, but obtaining high throughput. When there are fewer thread conflicts, similar performance can be obtained; when there are serious thread conflicts, the performance is much higher than that of CAS.
The above summary of Java concurrent programming - using CAS carefully is the detailed explanation of the editor. I hope it can give you a reference and I hope you can support Wulin.com more.