This article was compiled by Colodoo (Paper Umbrella)

QQ 425343603

Java Learning Exchange Group (717726984)

Reference Book the Art of Concurrent Programming in Java

The notes will be accompanied by the original text, so if you don’t want to read the original text, you can just read the summary of the list

Context switch

  • Single-core processors support multithreading

  • The CPU supports multithreading by allocating CPU time slices to each thread

  • A time slice is the amount of time that the CPU allocates to each thread

  • The short time slice and non-stop switching make it feel like multiple threads are executing simultaneously

  • Time slices are typically tens of milliseconds (ms)

  • The CPU uses the time slice allocation algorithm to execute tasks in a cycle. When a task points to a time slice, it moves to the next task

  • Before switching, the state of the previous task is saved so that the task state can be loaded next time

  • The process from saving to reloading a task is a context switch

This is just like when we read two trees at the same time. When we read an English technical book, we find a word we do not know, so we open the Chinese and English fields. But before we put down the English technical book, our brain must remember how many pages and lines we have read in this book. Such a switch can affect reading efficiency, just as context switching can affect thread execution speed.

  • Context switching affects thread execution speed

Is multithreading necessarily fast

public class ConcurrencyTest {
    
    private static final long count = 1000000001;
    public static void main(String[] args) throws InterruptedException {
        concurrency();
        serial();
    }
    
    private static void concurrency(a) throws InterruptedException {
        long start = System.currentTimeMillis();
        Thread thread = new Thread(new Runnable() {
            public void run(a) {
                int a = 0;
                for(long i = 0; i < count; i++) {
                    a += 5; }}}); thread.start();int b = 0;
        for(long i = 0; i < count; i++) {
            b--;
        }
        long time = System.currentTimeMillis() - start;
        thread.join();
        System.out.println("concurrency: " + time + "ms, b=" + b);
    }
    
    private static void serial(a) {
        
        long start = System.currentTimeMillis();
        int a = 0;
        for(long i = 0; i < count; i++) {
            a += 5;
        }
        int b = 0;
        for(long i = 0; i < count; i++) {
            b--;
        }
        long time = System.currentTimeMillis() - start;
        System.out.println("serial: " + time + "ms, b=" + b + ", a="+ a); }}Copy the code

  • Threads have the overhead of creating and switching contexts

Tests the number and duration of context switches

How do I reduce context switches

  • Lockless concurrent programming
  • CAS algorithm
  • Using minimum threads
  • coroutines

Lockless concurrent threads

When multiple threads compete for locks, context switch will be caused. Therefore, when multiple threads process data, some methods can be used to avoid using locks. For example, the ID of data can be divided according to the Hash algorithm, and different threads process different data.

CAS algorithm

Java’s Atomic package uses the CAS algorithm to update data without locking.

Using minimum threads

Creating unnecessary threads, such as a few tasks but creating many threads to process them, can result in a large number of threads waiting.

coroutines

In a single thread to achieve multi-task scheduling, and then in a single thread to maintain the switch between multiple tasks.

Reduce context switch combat

  • Reduce the number of threads WAITING on line to reduce context switching.
$ jps
$ jstack 15124 | awk '{print $2$3$4$5}'
22:39:51
threaddumpJavaHotSpot(TM)

#13prio=5os_prio=0tid=0x0000000003377800
RUNNABLE

eventloop"#11prio=5 RUNNABLE sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(NativeMethod) sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:296) sun.nio.ch.WindowsSelectorImpl$SubSelector.access$400(WindowsSelectorImpl.java:278) sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:159) sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) locked<0x00000000f785d1c8>(aio.netty.channel.nio.SelectedSelectionKeySet) locked<0x00000000f785ed40>(ajava.util.Collections$UnmodifiableSet) locked<0x00000000f785e1f0>(asun.nio.ch.WindowsSelectorImpl) sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) io.netty.channel.nio.SelectedSelectionKeySetSelector.select(SelectedSelectionKeySetSelector.java:62) io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:765) io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:413) io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:909) java.lang.Thread.run(Thread.java:748) Thread"#10daemonprio=9
RUNNABLE

CompilerThread3"#9daemonprio=9 RUNNABLE CompilerThread2"#8daemonprio=9
RUNNABLE

CompilerThread1"#7daemonprio=9 RUNNABLE CompilerThread0"#6daemonprio=9
RUNNABLE

Listener"#5daemonprio=5 RUNNABLE Dispatcher"#4daemonprio=9
RUNNABLE

#3daemonprio=8os_prio=1
WAITING(onobjectmonitor)
java.lang.Object.wait(NativeMethod)
waitingon<0x00000000f77185e8>(a
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
locked<0x00000000f77185e8>(ajava.lang.ref.ReferenceQueue$Lock)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

Handler"#2daemonprio=10 WAITING(onobjectmonitor) java.lang.Object.wait(NativeMethod) waitingon<0x00000000f7720178>(a java.lang.Object.wait(Object.java:502) java.lang.ref.Reference.tryHandlePending(Reference.java:191) locked<0x00000000f7720178>(ajava.lang.ref.Reference$Lock) java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) Thread"os_prio=2tid=0x000000000346e800nid=0x4384
Copy the code

A deadlock

public class DeadLockDemo {
    private static String A = "A";
    private static String B = "B";
    public static void main(String[] args) {
        new DeadLockDemo().deadLock();
    }

    public void deadLock(a) {
        Thread t1 = new Thread(new Runnable() {
            public void run(a) {
                synchronized(A) {
                    try {
                        Thread.currentThread().sleep(200);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized(B) {
                        System.out.println("1"); }}}}); Thread t2 =new Thread(new Runnable() {
            public void run(a) {
                synchronized(B) {
                    synchronized(A) {
                        System.out.println("2"); }}}}); t1.start(); t2.start(); }}Copy the code

There are several reasons why deadlocks can occur

  • Failed to release the lock due to an anomaly after getting the lock
  • An exception occurred while releasing the lock

Effects of deadlocks and checking methods

  • Once a deadlock occurs, services are unavailable
  • Check for problems with the dump thread

Common ways to avoid deadlocks

  • Avoid one thread acquiring multiple locks at the same time
  • Avoid a thread occupying multiple resources in a lock at the same time. Try to ensure that each lock occupies only one resource
  • Try using a timing lock, using lock.tryLock(timeout) instead of using an internal locking mechanism
  • For database locks, gasol and unlock must be in a database connection, otherwise the unlock will fail

Challenges of resource constraints

What are resource limits

Resource limitation means that the execution speed of the program is limited by computer hardware resources or software resources during concurrent programming. For example, if the bandwidth of the server is only 2Mb/s and the download speed of a resource is 1Mb/s, the system starts 10 threads to download the resource and the download speed will not become 10 MB /s. Therefore, the limitation of these resources should be taken into account when performing concurrent programming. Hardware resources limit the upload/download speed, disk read/write speed, and CPU processing speed. Software resources are limited by the number of database connections and socket connections.

Problems caused by resource constraints

In concurrent programming, the principle of the code execution speed is serial execution part of the code into concurrent execution, but if the serial code will be a period of concurrent execution, because of limited resources, is still in serial implementation, this program not only speed up execution, it will more slowly, because of the increased context switch time and resource scheduling. For example, when I saw a program using multiple threads to concurrently download and process data on the office network, the CPU utilization reached 100% and the task could not be completed within several hours. Later, it was modified to single thread and completed in one hour.

How to solve the problem of resource constraints

For hardware resource constraints, consider clustering parallel execution programs. Since resources are limited on a single machine, let the program run on multiple machines. Such as using ODPS, Hadoop, or building your own server cluster, where different machines process different data. By “data ID% number of machines”, a machine number can be calculated and then processed by the corresponding machine number. For software resource constraints, you can use resource pools to reuse resources. Such as using connection pooling to reuse database and Socket connections, or establishing only one connection when calling each other’s WebService interface for data.

Concurrent programming under resource constraints

  • Adjust the concurrency of the program for different resource limits

  • The file download program depends on two resources – bandwidth and disk read/write speed.

  • When there is a database operation, the number of database connections is involved. If the SQL statement is executed very fast and the number of threads is much larger than the number of database connections, some threads will be blocked waiting for the database connection.