In addition to using synchronized and Lock to Lock, there are many tool classes in Java that can solve concurrency problems without locking.
1. Atomic tools
In JDK 1.8, the classes under the java.util.concurrent.atomic package are all atomic classes, which are implemented based on sun.misc.Unsafe.
- In order to solve the concurrency problem, CPU provides CAS instruction, full name is Compare And Swap, that is, compare and interact.
- CAS instruction requires three parameters: variable, comparison value and new value. When the current value of a variable is equal to the comparison value, the variable is updated to the new value
- CAS is a CPU instruction, which ensures atomicity at the CPU hardware level.
- The atoms in java.util.concurrent.atomic package are divided into atomic basic data type, atomic object reference type, atomic array, atomic object attribute updater and atomic accumulator.
Atomic basic data types: AtomicBoolean, AtomicInteger, AtomicLong
Atomic object reference types: AtomicReference, AtomicStampedReference, AtomicMarkableReference
Atomic array: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
Atomic object property update: AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater
Atomic accumulator: doubleacumulator, DoubleAdder, LongAccumulator, LongAdder
Modify the class we used to test atomicity problems, and use a simple example of AtomicInteger.
package constxiong.concurrency.a026; import java.util.concurrent.atomic.AtomicInteger; /** * Test atomic class AtomicInteger * * @author ConstXiong */ public class TestAtomicInteger { // Enumeration variables static volatile AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { // Add 10000 to count for thread 1 Thread t1 = new Thread(() -> { for (int j = 0; j < 10000; j++) { count.incrementAndGet(); } System.out.println("thread t1 count Add 10000 to end"); }); // Thread 2 adds 10000 to count Thread t2 = new Thread(() -> { for (int j = 0; j < 10000; j++) { count.incrementAndGet(); } System.out.println("thread t2 count Add 10000 to end"); }); // Start thread 1 t1.start(); // Start thread 2 t2.start(); // Wait for thread 1 to finish executing t1.join(); // Wait for thread 2 to complete t2.join(); // Print count variable System.out.println(count.get()); } }
Print results as expected
thread t2 count plus 10000 ends thread t1 count + 10000 20000
2. Thread local storage
- The java.lang.ThreadLocal class is used for thread localized storage.
- Thread localized storage is to create a variable for each thread. Only this thread can view and modify the value in this variable.
- A typical example is that when spring deals with database transactions, it uses ThreadLocal to store its own database Connection for each thread.
- When using ThreadLocal, please note that when not using the variable, you must call the remove() method to remove the variable, otherwise it may cause memory leakage.
Example
package constxiong.concurrency.a026; /** * Test atomic class AtomicInteger * * @author ConstXiong */ public class TestThreadLocal { // Thread local store variable private static final ThreadLocal<Integer> THREAD_LOCAL_NUM = new ThreadLocal<Integer>() { @Override protected Integer initialValue() {//Initial value return 0; } }; public static void main(String[] args) { for (int i = 0; i < 3; i++) {// Start three threads Thread t = new Thread() { @Override public void run() { add10ByThreadLocal(); } }; t.start(); } } /** * Thread local storage variable plus 5 */ private static void add10ByThreadLocal() { try { for (int i = 0; i < 5; i++) { Integer n = THREAD_LOCAL_NUM.get(); n += 1; THREAD_LOCAL_NUM.set(n); System.out.println(Thread.currentThread().getName() + " : ThreadLocal num=" + n); } } finally { THREAD_LOCAL_NUM.remove();// Remove variables } } }
The last value of each thread is printed to 5
Thread-0 : ThreadLocal num=1 Thread-2 : ThreadLocal num=1 Thread-1 : ThreadLocal num=1 Thread-2 : ThreadLocal num=2 Thread-0 : ThreadLocal num=2 Thread-2 : ThreadLocal num=3 Thread-0 : ThreadLocal num=3 Thread-1 : ThreadLocal num=2 Thread-0 : ThreadLocal num=4 Thread-2 : ThreadLocal num=4 Thread-0 : ThreadLocal num=5 Thread-1 : ThreadLocal num=3 Thread-2 : ThreadLocal num=5 Thread-1 : ThreadLocal num=4 Thread-1 : ThreadLocal num=5
3,copy-on-write
- It can be seen from the English name that copying when writing is needed reflects a delay strategy.
- The copy on write containers in Java include CopyOnWriteArrayList and CopyOnWriteArraySet.
- It involves the full replication of arrays, so it also consumes more memory and is suitable for use when there is less writing.
This is a simple example of CopyOnWriteArrayList. It just shows how to use CopyOnWriteArrayList, and it is thread safe. This scenario is not suitable to use CopyOnWriteArrayList because it is more write than read.
package constxiong.concurrency.a026; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; /** * Test copy on write * @author ConstXiong */ public class TestCopyOnWrite { private static final Random R = new Random(); private static CopyOnWriteArrayList<Integer> cowList = new CopyOnWriteArrayList<Integer>(); // private static ArrayList<Integer> cowList = new ArrayList<Integer>(); public static void main(String[] args) throws InterruptedException { List<Thread> threadList = new ArrayList<Thread>(); //Start 1000 threads and add 5 random integers to the cowList for (int i = 0; i < 1000; i++) { Thread t = new Thread(() -> { for (int j = 0; j < 5; j++) { //Sleep for 10 milliseconds, and let the thread add integers to the cowList at the same time, which leads to concurrency problems. try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } cowList.add(R.nextInt(100)); } }) ; t.start(); threadList.add(t); } for (Thread t : threadList) { t.join(); } System.out.println(cowList.size()); } }
Print results
5000
If put
private static CopyOnWriteArrayList<Integer> cowList = new CopyOnWriteArrayList<Integer>();
Change to
private static ArrayList<Integer> cowList = new ArrayList<Integer>();
The printing result is an integer less than 5000.
Java interview question summary, there is always a stuck you!