Java geek

Related reading:

Java concurrent programming (A) knowledge map

Java concurrent programming atomicity Java concurrent programming visibility Java Concurrent programming Ordering Java Concurrent Programming Creating threads Overview Java Concurrent Programming Introduction (synchronized Java Concurrent Programming (8) Thread Life Cycle (9) Java Concurrent programming (9) Deadlock and deadlock bits Java concurrent programming (10) Java concurrent programming (10) Lock optimization Java Concurrent Programming introduction (11) Stream limiting scenarios and Spring Stream Limiting Scenarios (12) Producer and Consumer patterns – Java concurrent Programming Introduction (13) Read/write locks and cache templates Java Concurrent Programming Introduction (14) CountDownLatch application scenarios CyclicBarrier is an asynchronous task scheduling tool, with the CompleteFeature. You can use the CyclicBarrier to complete tasks. You can use the CyclicBarrier to complete tasks Introduction to Java Concurrent Programming Common Locking Scenarios and Locking Tools Introduction to Java Concurrent Programming Volatile keywords Introduction to Java Concurrent Programming 22 The ThreadLocal variable Daemon thread


1. Java atomic classes

Java atomic classes in Java. Util. Concurrent. Atomic package, use no locking mechanism to realize the atomic operation, so as to be thread-safe.

The lockless mechanism provides higher performance than the locking mechanism.

2. AtomicInteger

The following example compares the results of the unlocked operation of atomic and primitive types.

Code:

import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerDemo { private volatile static int count = 0; private static AtomicInteger atomicInteger = new AtomicInteger(); private static CountDownLatch countDownLatch; private static int CYCLE_TIMES = 20000; private static int THREAD_NUMS = 2; private static class AtomicCounter implements Runnable { @Override public void run() { for (int i = 0; i < CYCLE_TIMES; i++) { atomicInteger.incrementAndGet(); Countdownlatch.countdown (); countdownlatch.countdown (); countdownlatch.countdown (); } } private static class PrimitiveTypeCounter implements Runnable { @Override public void run() { for (int i = 0; i < CYCLE_TIMES; i++) { count = count + 1; Countdownlatch.countdown (); countdownlatch.countdown (); countdownlatch.countdown (); } } private static class PrimitiveTypeSyncCounter implements Runnable { @Override public void run() { for (int i = 0; i < CYCLE_TIMES; I++) {synchronized (atomicintegerdemo.class) {// count = count + 1; } } countDownLatch.countDown(); } } private static void execCount(Runnable runnable) throws InterruptedException { count = 0; countDownLatch = new CountDownLatch(THREAD_NUMS); for (int i = 0; i < THREAD_NUMS; i++) { new Thread(runnable).start(); } countDownLatch.await(); } public static void main(String[] args) throws InterruptedException { execCount(new AtomicCounter()); System.out.println("atomicInteger: " + atomicInteger.get()); execCount(new PrimitiveTypeCounter()); System.out.println("primitive type: " + count); execCount(new PrimitiveTypeSyncCounter()); System.out.println("primitive type sync: " + count); }}Copy the code

Output:

atomicInteger: 40000
primitive type: 26664
primitive type sync: 40000
Copy the code

As you can see, the atomic class does not need a lock to get a correct result, whereas the primitive type must be locked to get a correct result.

3. The AtomicReference example

AtomicReference is used to provide the ability to operate atomically on Java objects.

Code:

import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; public class AtomicReferenceDemo { private static AtomicReference<Person> atomicReference = new AtomicReference(); private static CountDownLatch countDownLatch; private static class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() ! = o.getClass()) return false; Person person = (Person) o; return age == person.age && name.equals(person.name); } @Override public int hashCode() { return Objects.hash(name, age); } } private static class AtomicRunnable implements Runnable { Person expect; Person upate; private AtomicRunnable(Person expect, Person upate) { this.expect = expect; this.upate = upate; } @Override public void run() { boolean isChanged = atomicReference.compareAndSet(expect, upate); System.out.println("AtomicRunnable isChanged: "+ isChanged +" "+ atomicReference.get()); countDownLatch.countDown(); } } private static class NormalRunnable implements Runnable { Person expect; Person upate; private NormalRunnable(Person expect, Person upate) { this.expect = expect; this.upate = upate; } @override public void run() {if (expect.name.equals(" Leo ") && expect. Age == 20) {} @override public void run() {if (expect.name. Non-thread-safe system.out. println("NormalRunnable isChanged: true "+ upate); } else { System.out.println("NormalRunnable isChanged: false " + expect); } countDownLatch.countDown(); } } private static void exec(Runnable[] runnables) throws InterruptedException { countDownLatch = new CountDownLatch(2);  for (int i = 0; i < 2; i++) { new Thread(runnables[i]).start(); } countDownLatch.await(); } public static void main(String[] args) throws InterruptedException { Person expect = new Person("leo", 20); atomicReference.set(expect); Person[] changePersons = new Person[]{ new Person("Jack", 30), new Person("Mark", 40) }; System.out.println("expect: " + expect); exec(new AtomicRunnable[] {new AtomicRunnable(expect, changePersons[0]), new AtomicRunnable(expect, changePersons[1])}); exec(new NormalRunnable[] {new NormalRunnable(expect, changePersons[0]), new NormalRunnable(expect, changePersons[1])}); }}Copy the code

Output:

expect: Person{name='leo', age=20}
AtomicRunnable isChanged: true Person{name='Mark', age=40}
AtomicRunnable isChanged: false Person{name='Mark', age=40}
NormalRunnable isChanged: true Person{name='Mark', age=40}
NormalRunnable isChanged: true Person{name='Jack', age=30}
Copy the code

As you can see, the AtomicReference is guaranteed atomicity when compareAndSet is performed. If thread A changes the object, thread B immediately sees the result of the change and does not continue to change it. Without the AtomicReference, both threads modify the object.

4. AtomicReferenceFieldUpdater example

AtomicReferenceFieldUpdater AtomicReference role similar to that is the difference between it and the AtomicReference can be seen from the name, it changes and Java object field atomic operation ability.

Code:

import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; public class AtomicReferenceFieldUpdaterDemo { private static AtomicReferenceFieldUpdater<Person, String> atomicRefFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Person.class, String.class, "name"); private static CountDownLatch countDownLatch; private static class Person { volatile String name; // It must be volatile and cannot be private int age; Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } } private static class AtomicRunnable implements Runnable { Person person; String expect; String upate; private AtomicRunnable(Person person, String expect, String upate) { this.person = person; this.expect = expect; this.upate = upate; } @Override public void run() { boolean isChanged = atomicRefFieldUpdater.compareAndSet(person, expect, upate); System.out.println("isChanged: "+ isChanged +" "+ person); countDownLatch.countDown(); } } private static void exec(Runnable[] runnables) throws InterruptedException { countDownLatch = new CountDownLatch(2);  for (int i = 0; i < 2; i++) { new Thread(runnables[i]).start(); } countDownLatch.await(); } public static void main(String[] args) throws InterruptedException { String expect = "loe"; Person person = new Person(expect, 20); String[] changeNames = new String[] { "Jack", "Mark" }; System.out.println("expect: leo"); exec(new AtomicRunnable[] {new AtomicRunnable(person, expect, changeNames[0]), new AtomicRunnable(person, expect, changeNames[1])}); }}Copy the code

Log printing:

expect: leo
isChanged: true Person{name='Jack', age=20}
isChanged: false Person{name='Jack', age=20}
Copy the code

As you can see, if thread A modifies an object field, thread B immediately sees the result of A’s modification and does not continue.

4. To summarize

1.Java atomic classes improve performance through the lockless mechanism. The mechanism is the same, but the use scenarios are different, based on Java primitive types, Java objects, a certain field of Java objects, and so on.

2. The core of the lockless mechanism is to ensure that CAS operations are atomic and thus thread-safe.


<– read left mark, left point like!