In the previous section, we mainly talked about the JVM memory model and the visibility of thread safety. This section focuses on the atomic operation of thread safety;
Competitive condition and critical region
Code example
public class IncrDemo { public int i = 0; public void incr() { i++; //Critical area } }
shared resource
Immutable object
Atomic operation definition
CAS mechanism
J. Atomic operation encapsulation class in U.C package
Thread safe code example
volatile thread safety example:
public class LockDemo1 { volatile int value = 0; // Direct operation of memory, modification of objects, array memory.... powerful API static Unsafe unsafe; private static long valueOffset; static { try { // Obtaining unsafe value by reflection technique Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); unsafe = (Unsafe) field.get(null); // Get the value attribute offset (used to determine the specific address of the value attribute in memory) valueOffset = unsafe.objectFieldOffset(LockDemo1.class.getDeclaredField("value")); } catch (Exception ex) { ex.printStackTrace(); } } public void add() { // TODO xx00 // i++;// JAVA level three steps // CAS + loop retry int current; do { // If the operation is time-consuming, the thread will take up a lot of CPU execution time current = unsafe.getIntVolatile(this, valueOffset); } while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1)); // May fail } public static void main(String[] args) throws InterruptedException { LockDemo1 ld = new LockDemo1(); for (int i = 0; i < 2; i++) { new Thread(() -> { for (int j = 0; j < 10000; j++) { ld.add(); } }).start(); } Thread.sleep(2000L); System.out.println(ld.value); } }
Example of synchronized thread safety:
public class LockDemo2 { int i = 0; public void add() { synchronized (this) { i++; } } public static void main(String[] args) throws InterruptedException { LockDemo2 ld = new LockDemo2(); for (int i = 0; i < 2; i++) { new Thread(() -> { for (int j = 0; j < 10000; j++) { ld.add(); } }).start(); } Thread.sleep(2000L); System.out.println(ld.i); } }
Lock thread safety example:
public class LockDemo3 { volatile int i = 0; Lock lock = new ReentrantLock(); public void add() { lock.lock(); try { i++; }finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { LockDemo3 ld = new LockDemo3(); for (int i = 0; i < 20; i++) { new Thread(() -> { for (int j = 0; j < 10000; j++) { ld.add(); } }).start(); } Thread.sleep(2000L); System.out.println(ld.i); } }
Example of atomic thread safety:
public class LockDemo4 { // volatile int i = 0; AtomicInteger i = new AtomicInteger(0); public void add() { // TODO xx00 // i + +; / / three steps i.incrementAndGet(); } public static void main(String[] args) throws InterruptedException { LockDemo4 ld = new LockDemo4(); for (int i = 0; i < 2; i++) { new Thread(() -> { for (int j = 0; j < 10000; j++) { ld.add(); } }).start(); } Thread.sleep(2000L); System.out.println(ld.i); } }
Comparison of thread safety operation efficiency
// Test case: run for 2 seconds at the same time, and check who has the most times public class LongAdderDemo { private long count = 0; // How to synchronize code blocks public void testSync() throws InterruptedException { for (int i = 0; i < 3; i++) { new Thread(() -> { long starttime = System.currentTimeMillis(); while (System.currentTimeMillis() - starttime < 2000) { // Run for two seconds. synchronized (this) { ++count; } } long endtime = System.currentTimeMillis(); System.out.println("SyncThread spend:" + (endtime - starttime) + "ms" + " v" + count); }).start(); } } // Atomic mode private AtomicLong acount = new AtomicLong(0L); public void testAtomic() throws InterruptedException { for (int i = 0; i < 3; i++) { new Thread(() -> { long starttime = System.currentTimeMillis(); while (System.currentTimeMillis() - starttime < 2000) { // Run for two seconds. acount.incrementAndGet(); // acount++; } long endtime = System.currentTimeMillis(); System.out.println("AtomicThread spend:" + (endtime - starttime) + "ms" + " v-" + acount.incrementAndGet()); }).start(); } } // LongAdder mode private LongAdder lacount = new LongAdder(); public void testLongAdder() throws InterruptedException { for (int i = 0; i < 3; i++) { new Thread(() -> { long starttime = System.currentTimeMillis(); while (System.currentTimeMillis() - starttime < 2000) { // Run for two seconds. lacount.increment(); } long endtime = System.currentTimeMillis(); System.out.println("LongAdderThread spend:" + (endtime - starttime) + "ms" + " v-" + lacount.sum()); }).start(); } } public static void main(String[] args) throws InterruptedException { LongAdderDemo demo = new LongAdderDemo(); demo.testSync(); demo.testAtomic(); demo.testLongAdder(); } }
Console output (random):
SyncThread spend:2000ms v24158152 SyncThread spend:2000ms v24158151 SyncThread spend:2000ms v24184392 AtomicThread spend:2000ms v-112956795 AtomicThread spend:2000ms v-112956796 AtomicThread spend:2000ms v-112956797 LongAdderThread spend:2000ms v-151089455 LongAdderThread spend:2000ms v-151089455 LongAdderThread spend:2000ms v-151089455