Fight against Java multithreading and high concurrency-JUC

This article is a note made when learning Java multithreading and high concurrency knowledge.

This part has a lot of content and is divided into 5 parts according to the content:

  1. Multithreading basics
  2. JUC articles
  3. Synchronous container and concurrent container
  4. Thread Pool
  5. MQ articles

This article is a JUC article.

table of Contents

1 What is JUC?

2 Concurrency and parallelism

3 Use Callable to create threads

4 Lock

4.1 Lock

4.2 Fair locks and unfair locks

4.3 The problem of producers and consumers of locks

4.4 Read-write lock

5 threads and eight locks

6 Commonly used auxiliary classes

6.1 CountDownLatch

6.2 CyclicBarrier

6.3 Semaphore


1 What is JUC?

java.util.concurrent, Java Concurrency Toolkit. mainly includes:

  • java.util.concurrent
  • java.util.concurrent.atomic
  • java.util.concurrent.locks

 

2 Concurrency and parallelism

Concurrency: Multiple threads quickly rotate execution, making it have the effect of multiple threads executing at the same time in a macroscopic view.

Parallel: Under multi-core CPU, multiple threads execute at the same time.

 

3 Use Callable to create threads

A Callable interface is provided in java.util.concurrent to create threads.

Compared with the thread created by the Runnable interface, the thread created by the Callable interface is:

  • Can have a return value
  • Can throw an exception

Code demo:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
 
public class CreateThread {
    static class MyCallable implements Callable<Integer> { //泛型规定返回值类型
        @Override
        public Integer call() throws Exception { //重写call()方法,类似于Runnable接口中的run()方法
            System.out.println("Hello MyCallable");
            return 1024;
        }
    }
 
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask futureTask = new FutureTask(myCallable); //适配器模式
        new Thread(futureTask).start(); //调用start()方法启动线程
        //打印返回值
        Integer result = (Integer) futureTask.get();
        System.out.println(result);
    }
}

It should be noted that Integer result = (Integer) futureTask.get(); this line of code may cause thread blocking.

 

4 Lock

4.1 Lock

In addition to using the synchronized keyword, JUC provides another thread synchronization method-Lock.

Lock is an interface in the java.util.concurrent.locks package. It has 3 implementation classes:

  • ReentrantLock, reentrant lock
  • ReentrantReadWriteLock.ReadLock, reentrant read lock
  • ReentrantReadWriteLock.WriteLock, reentrant write lock

3 steps to use Lock:

  1. Lock lock = new ReentrantLock()
  2. lock.lock()
  3. lock.unlock(), if the unlock() method is not called to release the lock resource, it will cause a deadlock, which is generally placed in the finally code block.

Code demo:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
    public static void main(String[] args) {
        MyLock myLock = new MyLock();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                myLock.test();
            }).start();
        }
    }
}

class MyLock {
    private int number = 10;
    Lock lock = new ReentrantLock();

    public void test() {
        lock.lock();
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + " 操作后,number = " + --number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

operation result:

After Thread-0 operation, number = 9 After
Thread-3 operation, number = 8 After
Thread-4 operation, number = 7 After
Thread-5 operation, number = 6 After
Thread-7 operation, number = 5 After
Thread-1 operation , Number = 4
Thread-2 operation, number = 3
Thread-6 operation, number = 2
Thread-8 operation, number = 1
Thread-9 operation, number = 0

4.2 Fair locks and unfair locks

Fair lock: Multiple threads acquire locks in the order in which they apply for locks, and new threads will directly enter the waiting queue to queue up.

Unfair lock: The new thread will try to acquire the lock, and if it cannot acquire it, it will enter the waiting queue.

Synchronized locks are non-fair locks; lock locks are non-fair locks by default, and fair locks can also be used.

lock uses fair lock:

Lock lock = new ReentrantLock(true); 

4.3 The problem of producers and consumers of locks

Synchronized locks use the monitor methods (wait, notify, notifyAll) in the Object class to implement communication between threads, and similar monitor methods are provided in the java.util.concurrent.locks package.

Steps to use Lock monitor:

  1. Lock lock = new ReentrantLock()
  2. Condition condition = lock.newCondition()
  3. lock.lock()
  4. condition.await(), thread waiting, always appears in the loop.
  5. condition.signalAll(), notify other threads.
  6. lock.unlock() is generally written in a finally code block.

Producer and consumer issues:

Multiple threads operate the same variable number at the same time:

  • Every time the producer operates, number++
  • Each time the consumer operates, number--

When number == 0, consumers cannot operate on it.

Code demo:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test {
    public static void main(String[] args) {
        Data data = new Data();
        int n = 4; //消费者数目
        int k = 5; //每个消费者的消费额
        new Thread(() -> {
            for (int i = 0; i < n * k; i++) {
                data.increment();
            }
        }, "Producer").start(); //生产者
        for (int id = 0; id < n; id++) {
            new Thread(() -> {
                for (int i = 0; i < k; i++) {
                    data.decrement();
                }
            }, "Consumer" + id).start(); //消费者们
        }
    }
}

class Data {
    private int number = 0;
    Lock lock = new ReentrantLock(); //创建锁
    Condition condition = lock.newCondition(); //创建监视器

    public void increment() {
        try {
            lock.lock();
            while (number > 3) {
                condition.await(); //线程等待
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            condition.signalAll(); //通知其它线程
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void decrement() {
        try {
            lock.lock();
            while (number <= 0) {
                condition.await(); //线程等待
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            condition.signalAll(); //通知其它线程
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

4.4 Read-write lock

The read-write lock ReadWriteLock is essentially a fine-grained Lock lock, which is divided into read locks and write locks:

  • Read lock: Also called shared lock, it can be occupied by multiple threads at the same time. Used for read-only operations.
  • Write lock: also called exclusive lock, exclusive lock, only allowed to be occupied by one thread. Used for write operations.

When multiple threads read and write the same resource:

  • Allow multiple threads to perform read operations at the same time
  • Multiple threads are not allowed to perform write operations at the same time
  • Do not allow read and write operations to be performed at the same time

The above requirements can be met by using a read-write lock.

Steps to use read lock:

  • ReadWriteLock readWriteLock = new ReentrantReadWriteLock()
  • readWriteLock.readLock().lock()
  • readWriteLock.readLock().unlock(), usually written in a finally code block.

Steps to use write lock:

  • ReadWriteLock readWriteLock = new ReentrantReadWriteLock()
  • readWriteLock.writeLock().lock()
  • readWriteLock.writeLock().unlock() is generally written in a finally code block.

Code demo:

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * ReadWriteLock
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.write(temp + "", temp);
            }).start();
        }
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "读取结果:" + myCache.read(temp + ""));
            }).start();
        }
    }
}

/**
 * 自定义缓存
 */
class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    public void write(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入完成");
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    public Object read(String key) {
        Object o;
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "读取");
            o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取完成");
        } finally {
            readWriteLock.readLock().unlock();
        }
        return o;
    }
}

operation result:

Thread-0 write
Thread-0 write complete
Thread-2 write
Thread-2 write complete
Thread-1 write
Thread-1 write complete
Thread-3 write
Thread-3 write complete
Thread-4 write
Thread -4 Write complete
Thread-5 read
Thread-5 read complete
Thread-9 read
Thread-9 read complete
Thread-5 read result: 0
Thread-6 read
Thread-6 read complete
Thread-8 read take
Thread-8 read completion
Thread-9 read result:. 4
Thread-7 read
Thread-7 read completion
Thread-7 reading result: 2
Thread-8 read result:. 3
the Thread-reading result. 6: 1

 

5 threads and eight locks

Eight thread locks are not eight actual locks, but eight classic problems about locks.

First question: In the following code, which operation A or operation B is executed first?

import java.util.concurrent.TimeUnit;

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Client client = new Client();
        new Thread(() -> {
            client.actionA();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            client.actionB();
        }).start();
    }
}

class Client {
    public synchronized void actionA() {
        System.out.println("执行操作A");
    }

    public synchronized void actionB() {
        System.out.println("执行操作B");
    }
}

Answer: Operation A. no doubt.

Second question: In the following code, should operation A or operation B be executed first?

import java.util.concurrent.TimeUnit;

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Client client = new Client();
        new Thread(() -> {
            client.actionA();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            client.actionB();
        }).start();
    }
}

class Client {
    public synchronized void actionA() {
        try {
            TimeUnit.SECONDS.sleep(2); //线程阻塞
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行操作A");
    }

    public synchronized void actionB() {
        System.out.println("执行操作B");
    }
}

Answer: Operation A. Although thread A is blocked for 2 seconds, it has already obtained the lock resource 2 seconds ago.

Third question: In the following code, should operation A, operation B or operation C be executed first?

import java.util.concurrent.TimeUnit;

public class Test3 {
    public static void main(String[] args) throws InterruptedException {
        Client client = new Client();
        new Thread(() -> {
            client.actionA();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            client.actionB();
        }).start();
        new Thread(() -> {
            client.actionC();
        }).start();
    }
}

class Client {
    //同步方法
    public synchronized void actionA() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行操作A");
    }
    //同步方法
    public synchronized void actionB() {
        System.out.println("执行操作B");
    }
    //普通方法
    public void actionC() {
        System.out.println("执行操作C");
    }
}

Answer: First operate C, then operate A, and finally operate B. Ordinary methods can be executed without acquiring lock resources.

Fourth question: In the following code, which operation A or operation B is executed first?

import java.util.concurrent.TimeUnit;

public class Test4 {
    public static void main(String[] args) throws InterruptedException {
        Client client1 = new Client();
        Client client2 = new Client();
        new Thread(() -> {
            client1.actionA(); //client1调用
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            client2.actionB(); //client2调用
        }).start();
    }
}

class Client {
    public synchronized void actionA() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行操作A");
    }

    public synchronized void actionB() {
        System.out.println("执行操作B");
    }
}

Answer: Operation B. The target of thread locking is the object, and there will be no lock conflicts between different objects.

Fifth question: In the following code, which operation A or operation B is executed first?

import java.util.concurrent.TimeUnit;

public class Test5 {
    public static void main(String[] args) throws InterruptedException {
        Client client = new Client();
        new Thread(() -> {
            client.actionA();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            client.actionB();
        }).start();
    }
}

class Client {
    //静态同步方法
    public static synchronized void actionA() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行操作A");
    }
    //静态同步方法
    public static synchronized void actionB() {
        System.out.println("执行操作B");
    }
}

Answer: Operation A. The thing to note is that the object locked by the static method is Client.class.

Sixth question: In the following code, which operation A or operation B is executed first?

import java.util.concurrent.TimeUnit;

public class Test6 {
    public static void main(String[] args) throws InterruptedException {
        Client client1 = new Client();
        Client client2 = new Client();
        new Thread(() -> {
            client1.actionA(); //client1调用
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            client2.actionB(); client2调用
        }).start();
    }
}

class Client {
    //静态同步方法
    public static synchronized void actionA() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行操作A");
    }
    //静态同步方法
    public static synchronized void actionB() {
        System.out.println("执行操作B");
    }
}

Answer: Operation A. The object locked by the static method is Client.class.

Seventh question: In the following code, which operation A or operation B is executed first?

import java.util.concurrent.TimeUnit;

public class Test7 {
    public static void main(String[] args) throws InterruptedException {
        Client client = new Client();
        new Thread(() -> {
            client.actionA();
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            client.actionB();
        }).start();
    }
}

class Client {
    //静态同步方法
    public static synchronized void actionA() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行操作A");
    }
    //同步方法
    public synchronized void actionB() {
        System.out.println("执行操作B");
    }
}

Answer: Operation B. no doubt.

Eighth question: In the following code, which operation A or operation B is executed first?

import java.util.concurrent.TimeUnit;

public class Test8 {
    public static void main(String[] args) throws InterruptedException {
        Client client1 = new Client();
        Client client2 = new Client();
        new Thread(() -> {
            client1.actionA(); //client1调用
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            client2.actionB(); //client2调用
        }).start();
    }
}

class Client {
    //静态同步方法
    public static synchronized void actionA() {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行操作A");
    }
    //同步方法
    public synchronized void actionB() {
        System.out.println("执行操作B");
    }
}

Answer: Operation B. no doubt.

 

6 Commonly used auxiliary classes

6.1 CountDownLatch

CountDownLatch is a universal synchronization tool. Its core idea is:

Allow one or more threads to wait until a set of operations in other threads are completed.

The key methods in the CountDownLatch class are:

  • countDown(), every time countDown() is called, the value of the CountDownLatch counter is -1.
  • await(), the current thread waits until the CountDownLatch counter is reset to zero, wake up the current thread.

Code demo:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        int count = 5;
        CountDownLatch countDownLatch = new CountDownLatch(count); //设置计数器countDownLatch的初始值

        for (int i = 0; i < count; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName());
                countDownLatch.countDown(); //countDownLatch的值-1
            }).start();
        }
        countDownLatch.await();//等待countDownLatch归零,再往下执行

        System.out.println("ok");
    }
}

6.2 CyclicBarrier

CyclicBarrier is also a universal synchronization tool. Its core idea is:

Allow a group of threads to all wait for each other to reach a common barrier point.

The key methods in the CyclicBarrier class are:

  • await()

Code demo:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        int count = 7;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(count, () -> { //设置计数器cyclicBarrier的阈值
            System.out.println("召唤神龙成功");
        });

        for (int i = 0; i < count; i++) {
            //lambda表达式不能直接操作 i
            final int temp = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "收集到" + (temp + 1) + "星球");
                try {
                    cyclicBarrier.await(); //cyclicBarrier的值+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "继续工作");
            }).start();
        }
    }
}

operation result:

Thread-0 collected 1 planet
Thread-4 collected 5 Planet
Thread-2 collected 3 planet
Thread-3 collected 4 planet
Thread-1 collected from 2 planet
Thread-5 collected 6 planet
Thread-6 collected 7 planet
call Dragon success
Thread-6 continues to operate
Thread-0 continues to work
Thread-4 continue to operate
Thread-3 continues to operate
Thread-2 continue to work
Thread-5 continue to operate
Thread-1 continue to work

6.3 Semaphore

Semaphore, semaphore, is usually used to limit the number of threads.

The semaphore maintains a set of licenses. If necessary, each thread will block until a license is obtained.

The key methods in the Semaphore class are:

  • acquire(), the current semaphore is -1, if the current semaphore is 0, wait.
  • release(), the current semaphore is +1.

Code demo:

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
    public static void main(String[] args) {
        int count = 2;
        Semaphore semaphore = new Semaphore(count);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "得到许可,开始了它的表演");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "表演结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}

operation result:

Thread-0 got permission, started its performance
Thread-1 got permission, started its performance
Thread-1 performance ended
Thread-0 performance ended
Thread-2 got permission, started its performance
Thread-3 got permission, started its performance
Thread-2 performance ended
Thread-3 performance ended
Thread-4 permission, it began its performances
Thread-4 performance ended
 

Learning video link: (this one is very good)

https://www.bilibili.com/video/BV1B7411L7tE

加油! (d • _ •) d

Guess you like

Origin blog.csdn.net/qq_42082161/article/details/113868802