Concurrent programming and high concurrency solution learning (thread safety - atomic Atomic)

1. Definition

        When a class is accessed by multiple threads , the class behaves correctly regardless of how the runtime environment is scheduled or how the processes will alternate , without requiring any additional synchronization or coordination in the calling code behavior, then the class is said to be thread-safe

Second, the three elements of thread safety:

Atomicity : Mutually exclusive access is provided, and only one thread can operate on it at a time
Visibility : A thread's changes to main memory can be observed by other threads in time
Orderliness : A thread observes the execution order of instructions in other threads. Due to the existence of instruction reordering, the observation results are generally disordered and out of order

3. Atomicity-Atomic package

       1.    AtomicXXX:CAS、Unsafe.compareAndSwapInt
import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

@ Slf4j
@ThreadSafe
public class AtomicExample1 {
    // total number of requests
    public static int clientTotal = 5000;
    // Number of threads executing concurrently at the same time
    public static int threadTotal = 200;
    public static AtomicInteger count = new AtomicInteger(0);
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }

    private static void add() {
        count.incrementAndGet();
        // count.getAndIncrement();
    }
}

AtomicLong

import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;

@ Slf4j
@ThreadSafe
public class AtomicExample2 {
    // total number of requests
    public static int clientTotal = 5000;
    // Number of threads executing concurrently at the same time
    public static int threadTotal = 200;
    public static AtomicLong count = new AtomicLong(0);
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }

    private static void add() {
        count.incrementAndGet();
        // count.getAndIncrement();
    }
}
LongAdder
import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.LongAdder;

@ Slf4j
@ThreadSafe
public class AtomicExample3 {
    // total number of requests
    public static int clientTotal = 5000;
    // Number of threads executing concurrently at the same time
    public static int threadTotal = 200;
    public static LongAdder count = new LongAdder();
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }
    private static void add() {
        count.increment();
    }
}

    2. Difference between AtomicLong and LongAdder

        For ordinary types of Long and double variables, jvm allows 64-bit read or write operations to be split into two 32-bit operations. The core of LongAddr is to separate hot data, and it can separate the core data value of AtomicLong into an array. When each thread accesses, it is mapped to one of the numbers through hash and other algorithms for counting, and the final calculation result is the summation and accumulation of this array. LongAdder distributes the update pressure of AtomicLong single point to each node, when low concurrency By directly updating the base, it can be well guaranteed that the performance of AtomicLong is basically the same . In high concurrency, improving dispersion improves performance .

Disadvantages of LongAddr: If there are concurrent updates during statistics, it may cause errors in the statistical data . When there is a high concurrent count in actual use, we can use LongAddr first instead of continuing to use AtomicLong. Of course, when thread competition is very low In the case of counting, using AtomicLong is simpler, more direct, and slightly more efficient.

AtomicReference

import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.LongAdder;

@ Slf4j
@ThreadSafe
public class AtomicExample4 {
    private static AtomicReference<Integer> count = new AtomicReference<>(0);
    public static void main(String[] args) {
        count.compareAndSet(0, 2); // 2
        count.compareAndSet(0, 1); // no
        count.compareAndSet(1, 3); // no
        count.compareAndSet(2, 4); // 4
        count.compareAndSet(3, 5); // no
        log.info("count:{}", count.get());
    }
}

AtomicIntegerFieldUpdater

import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReference;

@ Slf4j
@ThreadSafe
public class AtomicExample5 {
    private static AtomicIntegerFieldUpdater<AtomicExample5> updater = AtomicIntegerFieldUpdater.newUpdater(AtomicExample5.class, "count");
    @Getter
    public volatile int count = 100;
    public static void main(String[] args) {
        AtomicExample5 example5 = new AtomicExample5();
        if (updater.compareAndSet(example5, 100, 120)) {
            log.info("update success 1, {}", example5.getCount());
        }
        if (updater.compareAndSet(example5, 100, 120)) {
            log.info("update success 2, {}", example5.getCount());
        } else {
            log.info("update failed, {}", example5.getCount());
        }
    }
}

In the above example, the count property of the AtomicExample5 object is atomized

import com.mmall.concurrency.annoations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;

@ Slf4j
@ThreadSafe
public class AtomicExample6 {
    private static AtomicBoolean isHappened = new AtomicBoolean(false);
    // total number of requests
    public static int clientTotal = 5000;
    // Number of threads executing concurrently at the same time
    public static int threadTotal = 200;
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    test();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("isHappened:{}", isHappened.get());
    }
    // only execute once
    private static void test() {
        if (isHappened.compareAndSet(false, true)) {
            log.info("execute");
        }
    }
}

四、synchronized

synchronized: depends on JVM
Lock: Depends on special CPU instructions, the code implements ReentrantLock
Modified place:
Modified code block: code enclosed in curly braces, acting on the calling object
Modified method: the entire method, acting on the called object
Modified static method: the entire static method, acting on all objects
Modified class: The part enclosed in parentheses applies to all objects
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@ Slf4j
public class SynchronizedExample1 {
    // decorate a code block
    public void test1(int j) {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }

    // decorate a method
    public synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }

    public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test2(1);
        });
        executorService.execute(() -> {
            example2.test2(2);
        });
    }
}
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@ Slf4j
public class SynchronizedExample2 {
    // decorate a class
    public static void test1(int j) {
        synchronized (SynchronizedExample2.class) {
            for (int i = 0; i < 10; i++) {
                log.info("test1 {} - {}", j, i);
            }
        }
    }
    // decorate a static method
    public static synchronized void test2(int j) {
        for (int i = 0; i < 10; i++) {
            log.info("test2 {} - {}", j, i);
        }
    }
    public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> {
            example1.test1(1);
        });
        executorService.execute(() -> {
            example2.test1(2);
        });
    }
}

Five, atomic comparison

Synchronized: Uninterruptible lock, suitable for less competition, readability number
Lock: Interruptible lock, diversified synchronization, can maintain normal when competition is fierce
Atomic: can maintain normal when the competition is fierce, better than Lock performance; but only one value can be synchronized




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325324492&siteId=291194637