Atomic operation CAS

1. What is atomic operation?

Atomic operations are operations that cannot be interrupted by thread scheduling; Once this operation starts, it runs through to the end without any context switch.

2. How does Java implement atomic operations?

1. Use synchronized to lock the operation

Existing problems:

1. Blocked threads have a high priority

2. What if the thread holding the lock never releases the lock?

3, a lot of contention, CPU consumption, and deadlock or other security.

2. Loop CAS (Compare and swap) to implement atomic operations

CAS operations in Java are implemented using the CMPXCHG instruction provided by the processor. The basic idea of implementing spin CAS is to loop CAS until the operation succeeds.

2.1 Principle of CAS

CAS(Compare And Swap), the instruction level guarantees that this is an atomic operation

Three operators: A memory address V, A desired value A, and A new value B

If the value at address V is equal to the expected value A, address V is assigned A new value B. If not, nothing is done. Continuous CAS operations are performed in loops (dead-loops, spins)

2.2 CAS problem

1. ABA problems

A county might change A to B, and then another thread might change B to A. But the A is no longer the A we originally had.

Like drinking water, I got a glass of water, and then went to the toilet, and then my colleague drank my water and filled it for me, and when I came back, although there was still a glass of water on the table, it was not my cup anymore. So to solve this problem, we can add a version number to the address we use to indicate whether our variable has changed.

You can record the version number using AtomicStampedReference and AtomicMarkableReference

2. Cost

Spin is a performance drain

3. Atomic operations of only one shared variable can be guaranteed

2.3. Use of atomic operation classes in Jdk

Update basic type classes: AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference

Update array classes: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray

Update reference types: AtomicReference, AtomicMarkableReference, AtomicStampedReference

Atomic updates field types: AtomicReferenceFieldUpdater AtomicIntegerFieldUpdater, AtomicLongFieldUpdater (less)

Code examples:

import java.util.concurrent.atomic.AtomicStampedReference; /** * @Auther: BlackKingW * @Date: 2019/4/15 22:09 * @ Description: take the version number of atomic operation * / public class UseAtomicStampedReference {static AtomicStampedReference<String> asr = new AtomicStampedReference<>("BlackKingW",0); public static void main(String[] args) throws InterruptedException { final int oldStamp = asr.getStamp(); Final String oldReferenc = asr.getreference (); // Final String oldReferenc = asr.getreference (); System.out.println(oldReferenc+"==========="+oldStamp); Thread rightStampThread = new Thread(new Runnable() { @Override public void run() { System.out.println(thread.currentThread ().getName()) +" "+oldStamp+"-" +asr.compareAndSet(oldReferenc, oldReferenc+"Java", oldStamp, oldStamp+1)); }}); Thread errorStampThread = new Thread(new Runnable() { @Override public void run() { String reference = asr.getReference(); System.out.println(thread.currentThread ().getName() +" "+asr.getStamp()+"-" +asr.compareAndSet(reference, reference+"C", oldStamp, oldStamp+1)); }}); rightStampThread.start(); rightStampThread.join(); errorStampThread.start(); errorStampThread.join(); System.out.println(asr.getReference()+"==========="+asr.getStamp()); }}Copy the code

Explicit lock

1, the Lock Interfaces and core methods

Lock () is used to get the lock. If the lock has already been acquired by another thread, wait.

Unlock () releases the lock

TryLock (), which is used to attempt to acquire the lock, returns true if it succeeded, or if it failed (i.e. the lock has been acquired by another thread)

Lock interface and synchronized comparison

Synchronized: a built-in Java keyword that does not need to manually release the lock. Clean code,

Lock: is a class implemented that manually releases the Lock. And lock acquisition can be interrupted, have timeout lock acquisition, try to obtain lock mechanism.

Code examples:

import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Auther: BlackKingW * @Date: 2019/4/14 12:09 * @Description: */ public class LockDemo { private Lock lock = new ReentrantLock(); private int count; public void increament() { lock.lock(); try { count++; }finally { lock.unlock(); } } public synchronized void incr2() { count++; incr2(); }}Copy the code

If increament uses a lock, the code is relatively complex, and the lock must be released in finally, otherwise it may never be released, resulting ina deadlock.

2. ReentrantLock

Reentrant means that a thread that has acquired the lock can re-enter the locked code block. Internally through counters. For example, the code above

import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Auther: BlackKingW * @Date: 2019/4/15 22:09 * @Description: */ public class ReentrantLockDemo { private Lock lock = new ReentrantLock(); private int count; public void increament() { lock.lock(); try { count++; }finally { lock.unlock(); } } public synchronized void incr2() { count++; incr2(); } public synchronized void test3() { incr2(); }}Copy the code

Add a method test3 to call incr2. If the lock cannot be reentrant, incr2 cannot be called. Cause the program to never run. A reentrant lock is a block of code that can be accessed repeatedly by threads that have acquired the lock.

3. Fair locks and unfair locks.

Fair locks and unfair locks. Fairness refers to the acquisition of locks. If a lock is fair, the lock acquisition order should conform to the absolute time order on the request, which meets the FIFO

When multiple threads request a block of code to be locked, only one thread can hold the lock at the same time, so the other threads in the order of arrival, then the lock is fair. For example, ReentrantLock can specify whether it is a fair or unfair lock. Otherwise it’s an unfair lock. Such as synchronized.

Fair locks VS unfair locks

A fair lock is the first node in the synchronization queue to ensure the absolute sequence of resource requests. However, if a non-fair lock is used, the thread that has just released the lock may continue to acquire the lock next time, and other threads may never obtain the lock, resulting in “hunger”.

Fair lock To ensure the absolute sequence in time, frequent context switching is required. Non-fair lock reduces context switching and performance overhead. Therefore, by default, ReentrantLock selects an unfair lock to reduce some context switches and ensure higher throughput.

4, ReadWriteLock Interface and read/write lock ReentrantReadWriteLock

Are all locks owned by only one thread? Of course not. For example, ReentrantReadWriteLock read/write lock.

ReentrantReadWriteLock allows simultaneous access by multiple reader threads, but does not allow simultaneous access by a writer-reader thread or writer-thread.

The ReadWriteLock interface has two methods

Lock readLock(); Access to read lock

Lock writeLock(); Access to write lock

ReentrantReadWriteLock Implements the ReadWriteLock interface. Used to obtain read/write locks.

ReentrantLock and synchronized are exclusive locks that can be held by only one thread at a time. ReentrantReadWriteLock is also known as a shared lock that can be accessed by multiple threads.

Usage scenario: Read more and write less

Code sample

/** * @Auther: BlackKingW * @Date: 2019/4/15 22:09 * @Description: */ public class UseSyn implements GoodsService { private GoodsInfo goodsInfo; public UseSyn(GoodsInfo goodsInfo) { this.goodsInfo = goodsInfo; } @Override public synchronized GoodsInfo getNum() { SleepTools.ms(5); return this.goodsInfo; } @Override public synchronized void setNum(int number) { SleepTools.ms(5); goodsInfo.changeNumber(number); } } import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @Auther: BlackKingW * @Date: 2019/4/15 22:09 * @Description: */ public class UseRwLock implements GoodsService { private GoodsInfo goodsInfo; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock getLock = lock.readLock(); Private final Lock setLock = lock.writeLock(); // Write lock public UseRwLock(GoodsInfo GoodsInfo) {this. GoodsInfo = GoodsInfo; } @Override public GoodsInfo getNum() { getLock.lock(); try { SleepTools.ms(5); return this.goodsInfo; }finally { getLock.unlock(); } } @Override public void setNum(int number) { setLock.lock(); try { SleepTools.ms(5); goodsInfo.changeNumber(number); }finally { setLock.unlock(); } } } /** * @Auther: BlackKingW * @Date: 2019/4/14 12:09 * @Description: */ public interface GoodsService { public GoodsInfo getNum(); Public void setNum(int number); /** * @auther: BlackKingW * @date: 2019/4/15 22:09 * @description: */ public class GoodsInfo { private final String name; private double totalMoney; Private int storeNumber; Public GoodsInfo(String name, int totalMoney, int storeNumber) {this.name = name; this.totalMoney = totalMoney; this.storeNumber = storeNumber; } public double getTotalMoney() { return totalMoney; } public int getStoreNumber() { return storeNumber; } public void changeNumber(int sellNumber){ this.totalMoney += sellNumber*25; this.storeNumber -= sellNumber; } } import java.util.Random; import java.util.concurrent.CountDownLatch; /** * @Auther: BlackKingW * @Date:2019/4/15 22:09 * @Description: */ public class BusiApp { static final int readWriteRatio = 10; Static final int minthreadCount = 3; Static CountDownLatch = new CountDownLatch(1); Private static class implements Runnable{private GoodsService GoodsService; private GoodsService implements Runnable; public GetThread(GoodsService goodsService) { this.goodsService = goodsService; } @Override public void run() { // try { // latch.await(); // Catch (InterruptedException e) {//} long start = system.currentTimemillis (); for(int i=0; i<100; I++){goodsservice.getnum (); } system.out.println (thread.currentThread ().getName()+" (system.currentTimemillis ()-start)+"ms"); Private static class implements Runnable{private GoodsService implements Runnable; public SetThread(GoodsService goodsService) { this.goodsService = goodsService; } @Override public void run() { // try { // latch.await(); // Catch (InterruptedException e) {//} long start = system.currentTimemillis (); Random r = new Random(); for(int i=0; i<10; I++){sleeptools.ms (50); goodsService.setNum(r.nextInt(10)); } system.out.println (thread.currentThread ().getName() +" "+(system.currentTimemillis ()-start)+"ms---------"); } } public static void main(String[] args) throws InterruptedException { GoodsInfo goodsInfo = new GoodsInfo (" Cup ", 100000100, 00); GoodsService goodsService = new UseRwLock(goodsInfo); /*new UseSyn(goodsInfo); */ for(int i = 0; i<minthreadCount; i++){ Thread setT = new Thread(new SetThread(goodsService)); for(int j=0; j<readWriteRatio; j++) { Thread getT = new Thread(new GetThread(goodsService)); getT.start(); } SleepTools.ms(100); setT.start(); } //latch.countDown(); }}Copy the code

By modifying busiApp to use read/write locks,

GoodsService goodsService = new UseRwLock(goodsInfo);
Copy the code

The execution time is

Change busiApp to use the synchronized keyword

GoodsService goodsService = new UseSyn(goodsInfo);
Copy the code

The execution time is

ReentrantReadWriteLock and ReentrantLock support the following functions:

1) Support fair and unfair lock acquisition methods;

2) Reentrant support. The read thread can also acquire the read lock after acquiring the read lock. After acquiring the write lock, the writer thread can acquire both the write lock and the read lock again.

3) It is also allowed to degrade from write lock to read lock by acquiring write lock, then acquiring read lock, and finally releasing write lock. However, upgrading from a read lock to a write lock is not allowed;

4) Both read and write locks support interruption during lock acquisition;

5) Condition support. Write-only locks provide a Conditon implementation; Read the lock does not support Conditon, readLock (.) newCondition will throw an UnsupportedOperationException ().

5, Condition interface

Wait and Notify /notifyAll are discussed in my concurrent programming project iii – Concurrent Utility Classes for Threads. The Condition interface functions like wait and notify.

The main method of Condition is

Await () The current thread enters the wait state

Signal () wakes up a thread waiting on Condition

SignalAll () wakes up all threads waiting on the Condition

Await, signal, signalAll and wait, notify, notifyAll

Await, signal, signalAll: build on top of lock, need to bind lock lock before use. Notify exactly what object needs to be woken up. The signal () method is recommended when waking up

Wait, notify, and notifyAll: Set up on an Object. The lock of the Object must be obtained before the Object is used. Therefore, notifyAll() is recommended to be used to wake up the Object.

The code for

import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @Auther: BlackKingW * @Date: 2019/4/14 12:09 * @Description: */ public class ExpressCond { public final static String CITY = "ShangHai"; private int km; /* private String site; /* private Lock Lock = new ReentrantLock(); private Condition keCond = lock.newCondition(); private Condition siteCond = lock.newCondition(); public ExpressCond() { } public ExpressCond(int km, String site) { this.km = km; this.site = site; } public void changeKm(){lock. Lock ();} public void changeKm(){lock. try { this.km = 101; keCond.signal(); }finally { lock.unlock(); }} public void changeSite(){lock.lock();}} public void changeSite(){lock. try { this.site = "BeiJing"; siteCond.signal(); }finally { lock.unlock(); Public void waitKm(){lock. Lock (); try { while(this.km<=100) { try { keCond.await(); System.out.println("check km thread["+Thread.currentThread().getId() +"] is be notifed."); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }finally { lock.unlock(); } System.out.println("the Km is "+this.km+",I will change db"); } public void waitSite(){lock.lock(); try { while(CITY.equals(this.site)) { try { siteCond.await(); System.out.println("check site thread["+Thread.currentThread().getId() +"] is be notifed."); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }finally { lock.unlock(); } System.out.println("the site is "+this.site+",I will call user"); } } /** * @Auther: BlackKingW * @Date: 2019/4/14 12:09 * @Description: */ public class TestCond { private static ExpressCond express = new ExpressCond(0,ExpressCond.CITY); */ private static class CheckKm extends Thread{@override public void run() {express.waitkm ();  }} /* Check the thread of location change, do not meet the condition, */ private static class CheckSite extends Thread{@override public void run() {express.waitsite (); } } public static void main(String[] args) throws InterruptedException { for(int i=0; i<3; i++){ new CheckSite().start(); } for(int i=0; i<3; i++){ new CheckKm().start(); } Thread.sleep(1000); express.changeKm(); // Express mileage change}}Copy the code

Modify the example from the previous article to use Condition for notification. It can be found that when the mileage changes, it will accurately notify the change of the mileage and conduct corresponding business processing. Unlike notify, which might wake up a business waiting for a location change. The salesman is abnormal.

This chapter focuses on several display locks. And the concepts of reentrant lock, exclusive lock, shared lock and so on. Leeptools.ms (5); Thread.sleep can be used instead. Welcome to give advice.