JUC study notes

1. What is JUC?

java.util Toolkit

2. Process and thread

Process and thread

Process: the execution process of a program. It is a running activity of the program on a data set in the computer. It is the basic unit for resource allocation and scheduling of the system and the basis of the structure of the operating system. Narrow definition: a process is an instance of a running program.

Thread: it is the smallest unit that the operating system can schedule operations. It is included in the process and is the actual operation unit in the process. Multiple threads can be concurrent in a process.

Relationship between process and thread: each process has a corresponding thread. When executing a program, it actually executes a corresponding series of threads. Process is the smallest unit of resource allocation, and thread is the smallest unit of program execution.

  • Fundamental difference: process is the smallest unit of resource allocation, and thread is the smallest unit of program execution. When the computer executes a program, it will create a corresponding process for the program. When allocating resources, it will allocate resources in the unit of process. Each process has a corresponding thread. When executing a program, it is actually executing a series of threads.
  • Address space: a process has its own independent address space. Every time a process is started, the system will allocate address space and establish a data table to maintain code segments, stack segments and data segments; Threads do not have an independent address space. Threads of the same process share the address space of the process.
  • Resource ownership: resources between processes are independent; Threads in the same process share the resources of the process.
  • Execution process: each independent process has an entry for program operation, sequential execution sequence and program entry. However, threads cannot run independently. They must exist in the application program, and an application program provides multithreading execution control.
  • System overhead: the process system overhead is large, and the thread execution overhead is small.

Java Thread classes: Thread, Runnable, Callcble

Can Java really start threads? Can not open, called the local method, the underlying C + + implementation.

From thread Click in the source code of the start () method:

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
	// Local methods, underlying C + +, Java can not directly operate the hardware.
    private native void start0();

Concurrency and parallelism

1. Concurrency: single core CPU

2. Parallel: multi-core CPU

/**
 * @desc
 * @auth llp
 * @date 2022 9:13, January 6
 */
public class Test {
    public static void main(String[] args) {
        // Get the number of CPU cores
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

Several states of threads

// Thread.State view source code
public enum State {
		// newborn
        NEW,
		// function
        RUNNABLE,
		// block
        BLOCKED,
		// wait for
        WAITING,
		// Timeout wait
        TIMED_WAITING,
		// Termination, death
        TERMINATED;
    }

wait/sleep difference

1. From different classes

wait ==> Object

sleep ==> Thread

2. About lock release

wait: the lock will be released

Sleep: I won't release the lock. I sleep with the lock

3. The scope of use is different

wait: must be in sync block

Sleep: you can sleep anywhere

3. Lock lock

Traditional synchronized

Thread is a separate resource class without any affiliated operations!

  1. Properties and methods
/**
 * @desc The basic ticket selling example thread is a separate resource class without any affiliated operations! Properties and methods
 * @auth llp
 * @date 2022 January 7, 2014 14:26
 */
public class SaleTicketsDemo01 {
    public static void main(String[] args) {
        // Multithreaded operation
        final Tickets tickets = new Tickets();

        // Runnable - > @ functionalinterface functional interface
        new Thread( ()->{ for (int i = 0; i < 60; i++) tickets.sale(); }, "thread  A").start();
        new Thread( ()->{ for (int i = 0; i < 60; i++) tickets.sale(); }, "thread  B").start();
        new Thread( ()->{ for (int i = 0; i < 60; i++) tickets.sale(); }, "thread  C").start();
    }
}

// Resource class
class Tickets{
    // Properties and methods
    private int number = 50;

    // How to sell tickets
    // public void sale(){
    public synchronized void sale(){
        if (number > 0){
            System.out.println(Thread.currentThread().getName() + "Sold the second" + (number--) + "Tickets, remaining:" + number + "Ticket");
        }
    }
}

Lock interface

Examples in JDK 8 Chinese help documents:

Implementation class of Lock:

Underlying source code of reentrant lock constructor:

Fair lock: when adding a lock, the queuing problem is considered. According to the order of applying for the lock and according to the first in first out (FIFO), the thread applying for the lock first obtains the lock, and other threads enter the queue to wait for the release of the lock. When the lock is released, the thread at the head of the queue is awakened.

Unfair lock (default): when locking, the problem of queuing is not considered, and the lock is directly attempted. If the lock happens to be unlock ed at this time, you can get the lock directly regardless of whether there are other threads waiting; Otherwise, it will be transformed into a fair lock mode and enter the waiting queue.

/**
 * @desc Lock lock
 * @auth llp
 * @date 2022 January 7, 2015 15:17
 */
public class SaleTicketsDemo02 {
    public static void main(String[] args) {
        Tickets02 tickets02 = new Tickets02();
        // Runnable - > @ functionalinterface functional interface
        new Thread( ()->{ for (int i = 0; i < 60; i++) tickets02.sale(); }, "thread  A").start();
        new Thread( ()->{ for (int i = 0; i < 60; i++) tickets02.sale(); }, "thread  B").start();
        new Thread( ()->{ for (int i = 0; i < 60; i++) tickets02.sale(); }, "thread  C").start();
    }
}


// Lock
class Tickets02{
    // Properties and methods
    private int number = 50;

    Lock lock = new  ReentrantLock();
    
    // How to sell tickets
    public void sale(){
        // Lock
        lock.lock();
        try {
            // Business code
            if (number > 0){
                System.out.println(Thread.currentThread().getName() + "Sold the second" + (number--) + "Tickets, remaining:" + number + "Ticket");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();  // Unlock
        }
    }
}

The difference between synchronized and Lock

1. synchronized is a built-in keyword in Java, and Lock is a Java class

2. synchronized cannot obtain the state of the lock. Lock can judge whether to obtain the lock

3. synchronized will automatically release the lock. Lock must release the lock manually, otherwise it will deadlock!

4. synchronized if thread 1 gets the lock and is blocked, thread 2 will wait foolishly all the time. Lock does not necessarily wait tryLock();.

5. synchronized re-entry lock is non interruptible and unfair. Lock re-entry lock can judge whether the lock is unfair (you can set it yourself)

6. synchronized is suitable for a small number of synchronization code problems, and Lock is suitable for a large number of synchronization code problems.

4. Producer and consumer issues

synchronized version of producer and consumer issues (use while to prevent false wake-up)

/**
 * @desc
 * @auth llp
 * @date 2022 16:22, January 18
 */
public class Test01 {
    public static void main(String[] args) {
        Data data = new Data();
        // Runnable - > @ functionalinterface functional interface
        new Thread( ()->{
            try {
                for (int i = 0; i < 60; i++) data.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread  A").start();
        new Thread( ()->{
            try {
                for (int i = 0; i < 60; i++) data.decrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "thread  B").start();
    }
}

// Judge waiting for business notification
class Data{     // Digital resources
    private int number = 0;

    // +1
    public synchronized void increment() throws InterruptedException {
        while (number != 0){
            // Judgment waiting
            this.wait();
        }
        // business
        number++;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        // Notification (notify other threads, I + 1 finished)
        this.notifyAll();
    }
    // -1
    public synchronized void decrement() throws InterruptedException {
        while (number == 0){
            // Judgment waiting
            this.wait();
        }
        // business
        number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        // Notification (notify other threads, I-1 finished)
        this.notifyAll();
    }
}

Producer and consumer issues Lock Edition

Find the Condition class through Lock

Condition Object monitor method( wait,notify and notifyAll )Decompose into distinct objects to provide multiple wait set s for each Object by combining these objects with any [lock] implementation. Lock replaces the synchronized method and statement, and Condition replaces the Object monitor method.

A condition (also known as a condition queue or condition variable) provides a meaning for a thread to hang (i.e., let it "wait") until another thread that a state condition may now be true notifies it. Because access to this shared state information occurs in different threads, it must be protected, so some form of lock should be associated with the condition. The main attribute of waiting to provide a condition is to release the relevant lock atomically and suspend the current thread, just like object Wait did that.

The Condition instance is essentially bound to a lock. For a specific Lock Instance to obtain the Condition instance, please use its newCondition() method.

Code implementation:

/**
 * @desc
 * @auth llp
 * @date 2022 January 24, 2014 14:20
 */
public class Test02 {
    public static void main(String[] args) {
        Data02 data = new Data02();
        // Runnable - > @ functionalinterface functional interface
        new Thread( ()->{
            for (int i = 0; i < 60; i++) data.increment();
        }, "thread  A").start();
        new Thread( ()->{
                for (int i = 0; i < 60; i++) data.decrement();
        }, "thread  B").start();
        new Thread( ()->{
            for (int i = 0; i < 60; i++) data.increment();
        }, "thread  C").start();
        new Thread( ()->{
            for (int i = 0; i < 60; i++) data.decrement();
        }, "thread  D").start();
    }
}

// Judge waiting for business notification
class Data02{     // Digital resources
    private int number = 0;

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    // +1
    public void increment(){
        lock.lock();
        try {
            // Business code
            while (number != 0){
                // Judgment waiting
                condition.await();
            }
            // business
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            // Notification (notify other threads, I + 1 finished)
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    // -1
    public void decrement(){
        lock.lock();
        try {
            // Business code
            while (number == 0){
                // Judgment waiting
                condition.await();
            }
            // business
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            // Notification (notify other threads, I-1 finished)
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

Condition accurate notification and wake-up thread

Code test:

/**
 * @desc Condition Accurately notify A to execute and call B to execute and C to execute and A
 * @auth llp
 * @date 2022 January 24, 2014 14:47
 */
public class Test03 {
    public static void main(String[] args) {
        Data03 data03 = new Data03();
        new Thread( ()->{
            for (int i = 0; i < 10; i++) data03.printA();
        }, "thread  A").start();
        new Thread( ()->{
            for (int i = 0; i < 10; i++) data03.printB();
        }, "thread  B").start();
        new Thread( ()->{
            for (int i = 0; i < 10; i++) data03.printC();
        }, "thread  C").start();
    }
}

/**
 * @desc Resource class
 * @auth llp
 * @date 2022/1/24 14:48
 */
class Data03{
    private final Lock lock = new ReentrantLock();
    private final Condition conditionA = lock.newCondition();
    private final Condition conditionB = lock.newCondition();
    private final Condition conditionC = lock.newCondition();
    private int number = 1; // 1A 2B 3C

    public void printA(){
        lock.lock();
        try {
            // Business judgment waiting for execution notice
            while (number != 1){
                // wait for
                conditionA.await();
            }
            System.out.println(Thread.currentThread().getName() + "=> Execute printing");
            // Wake up, wake up the designated person
            number = 2;
            conditionB.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try {
            // Business judgment waiting for execution notice
            while (number != 2){
                // wait for
                conditionB.await();
            }
            System.out.println(Thread.currentThread().getName() + "=> Execute printing");
            // Wake up, wake up the designated person
            number = 3;
            conditionC.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try {
            // Business judgment waiting for execution notice
            while (number != 3){
                // wait for
                conditionC.await();
            }
            System.out.println(Thread.currentThread().getName() + "=> Execute printing");
            // Wake up, wake up the designated person
            number = 1;
            conditionA.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

5. 8 lock phenomenon

Eight questions about locks

1. Under standard conditions, do two threads print and send text messages or make phone calls first 1 / send text messages 2 / make phone calls

2. sendSms sleeps for 4 seconds. Do the two threads print and send text messages or call first 1 / send text messages 2 / make phone calls

/**
 * @desc
 * @auth llp
 * @date 2022 January 24, 2015 15:19
 * 1,Under standard conditions, do the two threads print and send text messages or make phone calls first-- 1 / send text messages 2 / make phone calls
 * 2,sendSms Sleep for 4 seconds. Do the two threads print and send text messages or call first-- 1 / send text messages 2 / make phone calls
 */
public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();

        new Thread(phone::sendSms, "thread  A").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(phone::call, "thread  B").start();
    }
}

class Phone{
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("send message");
    }

    public synchronized void call(){
        System.out.println("phone");
    }
}

The object of the synchronized lock is the caller of the method (* object lock). The two methods use the same lock. Who gets it first and who executes it

3. After adding a common method, do you want to send text messages or hello – 1/Hello 2 / send text messages first

4. Two objects, two synchronization methods and two threads. Do you print and send text messages or make phone calls first 1 / call 2 / text

/**
 * @desc
 * @auth llp
 * @date 2022 January 24, 2015 15:19
 * 3,After adding a common method, first execute SMS or hello -- common method
 * 4,Two objects, two synchronization methods, two threads, print and send text messages or call first-- 1 / call 2 / text
 */
public class Test02 {
    public static void main(String[] args) throws InterruptedException {
        // Two objects
        Phone02 phone1 = new Phone02();
        Phone02 phone2 = new Phone02();

        new Thread(phone1::sendSms, "thread  A").start();

        TimeUnit.SECONDS.sleep(1);

        // new Thread(phone1::hello, "thread B") start();
        new Thread(phone2::call, "thread  B").start();
    }
}

class Phone02{
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("send message");
    }

    public synchronized void call(){
        System.out.println("phone");
    }

    public void hello(){
        System.out.println("Hello");
    }
}

5. Add two static synchronization methods. There is only one object. Print and send text messages or call first?

6. Add two static synchronization methods and two objects. Print and send text messages or call first?

/**
 * @desc
 * @auth llp
 * @date 2022 January 24, 2015 15:57
 * 5,Add two static synchronization methods. There is only one object. Print, send text messages or call first?
 * 6,Add two static synchronization methods and two objects. Print and send text messages or call first?
 */
public class Test03 {
    public static void main(String[] args) throws InterruptedException {
        // Two objects
        Phone03 phone1 = new Phone03();
        Phone03 phone2 = new Phone03();

        new Thread(()->{
            phone1.sendSms();
        }, "thread  A").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone2.call();
        }, "thread  B").start();
    }
}

class Phone03{
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("send message");
    }

    public static synchronized void call(){
        System.out.println("phone");
    }

    public void hello(){
        System.out.println("Hello");
    }
}

static: when the Class is loaded, it is locked by the Class and the same lock is used. (* type lock)

7. A common synchronization method and a static synchronization method, an object, print, send text messages or call first

8. A common synchronization method and a static synchronization method, two objects, print and send text messages or call first

/**
 * @desc
 * @auth llp
 * @date 2022 16:06, January 24
 * 7,A common synchronization method and a static synchronization method, an object, print, send text messages or call first--
 * 8,A common synchronization method and a static synchronization method, two objects, print, send text messages or call first--
 */
public class Test04 {
    public static void main(String[] args) throws InterruptedException {
        // Two objects
        Phone04 phone1 = new Phone04();
        Phone04 phone2 = new Phone04();

        new Thread(()->{
            phone1.sendSms();
        }, "thread  A").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            phone2.call();
        }, "thread  B").start();
    }
}

class Phone04{
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("send message");
    }

    public synchronized void call(){
        System.out.println("phone");
    }

    public void hello(){
        System.out.println("Hello");
    }
}

Summary:

1. Ordinary methods are not affected by locks!

2. The synchronization method locks the caller of the object! (object lock)

3. The lock object of the static synchronization method is Class, and the Class template is unique, which is not affected by the number of objects created! (Class lock)

Object locks and class locks are different locks, so multithreading is asynchronous when executing the methods of these two different locks at the same time.

6. Collection class unsafe

list is not safe

/**
 * @desc java.util.ConcurrentModificationException Concurrent modification exception
 * @auth llp
 * @date 2022 10:48, January 25
 */
public class ListTest {
    public static void main(String[] args) {
        // ArrayList is not safe under concurrency
        // List<String> list = new ArrayList<>();
        /**
         * Solution:
         * 1,List<String> list = new Vector<>();
         * 2,List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3,List<String> list = new CopyOnWriteArrayList<>();
         */
        // List<String> list = new Vector<>();
        // List<String> list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();   // CopyOnWrite copy when writing. Avoid overwriting when writing, resulting in data problems
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }, String.valueOf(i)).start();

        }
    }
}

set is not safe

/**
 * @desc
 * @auth llp
 * @date 2022 January 25, 2011 11:08
 */
public class SetTest {
    public static void main(String[] args) {
        // Set<String> set = new HashSet<>();
        /**
         * resolvent:
         * 1,Set<String> set = Collections.synchronizedSet(new HashSet<>());
         * 2,Set<String> set = new CopyOnWriteArraySet<>();
         */
        // Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

map is not safe

/**
 * @desc
 * @auth llp
 * @date 2022 January 25, 2011 11:17
 */
public class MapTest {
    public static void main(String[] args) {
        // Map<String, String> map = new HashMap<>();
        /**
         * terms of settlement:
         * 1,Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
         * 2,Map<String, String> map = new ConcurrentHashMap<>();
         */
        // Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}

7,Callable

1. Can have return value

2. Can run out of exception

3. Different methods, run() = = > call()

Callable is a functional interface, and the return value of call() is the type of generic parameter.

Code test

/**
 * @desc
 * @auth llp
 * @date 2022 January 26, 2009 9:54
 */
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // new Thread(new Runnable()).start();
        // new Thread(new FutureTask(Callable<V> callable)).start();

        MyThread myThread = new MyThread();
        // Adaptation class
        FutureTask<String> futureTask = new FutureTask<>(myThread);
        new Thread(futureTask, "thread  A").start();
        new Thread(futureTask, "thread  B").start();		// The results will be cached to improve efficiency
        // Return value
        String s = futureTask.get();	// get() may cause blocking. Put it last! Or use asynchronous communication.
        System.out.println(s);

    }
}

class MyThread implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("call()");
        return "test";
    }
}

be careful:

1. The results are cached

2. Getting the returned results may cause blocking.

8. Common auxiliary classes

8.1 CountDownLatch

/**
 * @desc Calculation class
 * @auth llp
 * @date 2022 January 26, 2013 10:36
 */
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        // The total number of initializations can only be used when tasks have to be executed
        CountDownLatch latch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "Out of the classroom!");
                latch.countDown();      // Quantity - 1
            }, String.valueOf(i)).start();
        }

        // Wait for the counter to return to zero before executing downward
        latch.await();

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

latch.countDown() / / quantity - 1

latch.await() / / wait for the counter to return to zero, and then execute downward

Every time a thread calls countDown(), the number is - 1. If the counter is 0, await() will be awakened.

8.2 CyclicBarrier

/**
 * @desc
 * @auth llp
 * @date 2022 January 26, 2010 10:43
 */
public class CyclicBarrierTest {
    public static void main(String[] args) {
        // Gather seven dragon balls to summon the Dragon

        // Calling dragon thread
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> System.out.println("Summon dragon successfully!"));

        for (int i = 1; i <= 7; i++) {
           final int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "collect" + temp + "A dragon ball");
                try {
                    cyclicBarrier.await();      // wait for
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

This auxiliary class is counted by await(). After await(), this thread will be blocked and the code after await() will not be executed until the dragon is summoned.

Addition counter.

8.3 Semaphore

/**
 * @desc
 * @auth llp
 * @date 2022 At 10:54 on January 26
 */
public class SemaphoreTest {
    public static void main(String[] args) {
        // Number of threads: parking space! Current limit!
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                // acquire() gets
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "Get a parking space");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "Leave the parking space");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();  // Release
                }
            }, String.valueOf(i)).start();
        }
    }
}

acquire() obtains a permission from this semaphore and blocks the thread until a permission is provided, otherwise the thread will be blocked interrupt.

release() releases a license and returns it to the semaphore.

Function: multiple shared resources are mutually exclusive! Concurrent flow restriction, control the maximum number of threads!

9. Read write lock

ReadWriteLock

/**
 * @desc
 * @auth llp
 * @date 2022 11:08, January 26
 */
public class ReadWriteLockTest {
    public static void main(String[] args) {
        //MyCache myCache = new MyCache();
        MyCacheLock myCacheLock = new MyCacheLock();

        // write in
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(()->{
                //myCache.put(temp+"", temp);
                myCacheLock.put(temp+"", temp);
            }, String.valueOf(i)).start();
        }

        // read
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(()->{
                //myCache.get(temp+"");
                myCacheLock.get(temp+"");
            }, String.valueOf(i)).start();
        }
    }
}

/**
 * @desc Custom cache
 * @auth llp
 * @date 2022/1/26 11:08
 */
class MyCache{
    private volatile Map<String, Object> map = new HashMap<>();

    // Save, write
    public void put(String key, int value){
        System.out.println(Thread.currentThread().getName() + "write in" + key);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "Write succeeded!");
    }

    // Take, read
    public void get(String key){
        System.out.println(Thread.currentThread().getName() + "read" + key);
        map.get(key);
        System.out.println(Thread.currentThread().getName() + "Read successfully!");
    }
}

/**
 * @desc Custom cache locking
 * @auth llp
 * @date 2022/1/26 11:08
 */
class MyCacheLock{
    private volatile Map<String, Object> map = new HashMap<>();

    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // When saving and writing, only one thread is expected to write
    public void put(String key, int value){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "write in" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "Write succeeded!");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readWriteLock.writeLock().unlock();
        }
    }

    // Take, read, everyone can read
    public void get(String key){
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "read" + key);
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "Read successfully!");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readWriteLock.readLock().unlock();
        }
    }
}

10. Blocking queue

Write: if the queue is full, the wait must be blocked

Read: if the queue is empty, the wait must be blocked

When will blocking queues be used: multithreaded concurrent processing, thread pool

1. Four sets of API s
modeThrow exceptionThere is a return valueBlocking waitingTimeout wait
insertadd(e)offer(e)put(e)offer(e, time, unit)
removeremove()poll()take()poll(time, unit)
inspectelement()peek()Not availableNot available

Throw exception:

/**
 * @desc Throw exception
 * @auth llp
 * @date 2022/1/26 14:56
 */
public static void test1(){
    // Size of queue
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

    System.out.println(blockingQueue.add("a"));
    System.out.println(blockingQueue.add("b"));
    System.out.println(blockingQueue.add("c"));
    // IllegalStateException: Queue full: exception thrown!
    // System.out.println(blockingQueue.add("d"));

    System.out.println(blockingQueue.element());    // Returns the first element of the queue
    
    System.out.println(blockingQueue.remove());
    System.out.println(blockingQueue.remove());
    System.out.println(blockingQueue.remove());
    // java.util.NoSuchElementException: exception thrown
    System.out.println(blockingQueue.remove());
}

With return value:

/**
 * @desc There is a return value
 * @auth llp
 * @date 2022/1/26 15:05 
 */
public static void test2(){
    // Size of queue
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

    System.out.println(blockingQueue.offer("a"));
    System.out.println(blockingQueue.offer("b"));
    System.out.println(blockingQueue.offer("c"));
    // If no exception is thrown, false is returned
    // System.out.println(blockingQueue.offer("d"));

    System.out.println(blockingQueue.peek());   // Returns the first element of the queue
    
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    // No exception is thrown and null is returned
    System.out.println(blockingQueue.poll());
}

Blocking wait:

/**
 * @desc Blocking waiting
 * @auth llp
 * @date 2022/1/26 15:08
 */
public static void test3() throws InterruptedException {
    // Size of queue
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);


    blockingQueue.put("a");
    blockingQueue.put("b");
    blockingQueue.put("c");
    // blockingQueue.put("d");  //  Always blocked

    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    // System.out.println(blockingQueue.take());   //  Always blocked 
}

Timeout wait:

/**
 * @desc Timeout wait
 * @auth llp
 * @date 2022/1/26 15:08
 */
public static void test4() throws InterruptedException {
    // Size of queue
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

    blockingQueue.offer("a");
    blockingQueue.offer("b");
    blockingQueue.offer("c");
    blockingQueue.offer("d", 2, TimeUnit.SECONDS);  // Wait 2 seconds to exit

    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
}
2. Synchronization queue

There is no capacity. You must wait for an element to be taken out before you can put another element in it!

/**
 * @desc Unlike other blockingqueues, synchronous queues do not store elements
 * @auth llp
 * @date 2022 January 26, 2015 15:18
 */
public class SynchronousQueueTest {
    public static void main(String[] args) {
        BlockingQueue<String> synchronousQueue = new SynchronousQueue<>(); // Synchronization queue

        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName() + " put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + " put 2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + " put 3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);

                System.out.println(Thread.currentThread().getName() + " take "+ synchronousQueue.take());
                System.out.println(Thread.currentThread().getName() + " take "+ synchronousQueue.take());
                System.out.println(Thread.currentThread().getName() + " take "+ synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T2").start();
    }
}

11. Thread pool (key)

Thread pool: three methods, seven parameters and four rejection strategies

Pool technology

Improve performance by reusing threads

1. Three methods

Executors.newSingleThreadExecutor();        // Single thread
Executors.newFixedThreadPool(5);   			// Create a fixed thread pool size
Executors.newCachedThreadPool();            // Scalable
/**
 * @desc Executors Tools and 3 methods
 * @auth llp
 * @date 2022 January 26, 2014 15:44
 */
public class Demo01 {
    public static void main(String[] args) {
        // ExecutorService threadPool = Executors.newSingleThreadExecutor();//  Single thread
        // ExecutorService threadPool = Executors.newFixedThreadPool(5);//  Create a fixed thread pool size
        ExecutorService threadPool = Executors.newCachedThreadPool();// Scalable, strong in case of strength, weak in case of weakness

        try {
            // After using the thread pool, you should use the thread pool to create threads
            for (int i = 0; i < 10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }

            // Close the thread pool after using it
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}
2. Large parameter

Source code analysis:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

Essence: create ThreadPoolExecutor()

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

corePoolSize: core thread pool size

maximumPoolSize: maximum thread pool size

keepAliveTime: wait for a timeout, and it will be released if no one uses it

Unit: timeout unit

workQueue: blocking queue

threadFactory: thread factory. It is used to create threads. It is generally fixed

handler: reject policy

Create a thread

/**
 * @desc
 * @auth llp
 * @date 2022 16:05, January 26
 * new ThreadPoolExecutor.AbortPolicy()    // Reject policy. If the queue is full, it will not be processed and an exception will be thrown
 * new ThreadPoolExecutor.CallerRunsPolicy()       // Where did you come from? Where did you go!
 * new ThreadPoolExecutor.DiscardPolicy()        // If the queue is full, the task will be lost and no exception will be thrown
 *
 */
public class Demo02 {
    public static void main(String[] args) {
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()      // When the queue is full, try to compete with the earliest one. If the competition is successful, execute it, otherwise discard it. Will not abandon exceptions
        );

        try {
            // Maximum load: Deque + max throws an exception = = RejectedExecutionException
            for (int i = 1; i <= 10; i++) {
                poolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            poolExecutor.shutdown();
        }
    }
}
3. Four rejection strategies

new ThreadPoolExecutor.AbortPolicy()    			// Reject policy. If the queue is full, it will not be processed and an exception will be thrown
new ThreadPoolExecutor.CallerRunsPolicy()       	// Where did it come from? Go back and deal with it!
new ThreadPoolExecutor.DiscardOldestPolicy()     	// If the queue is full, the task will be lost and no exception will be thrown
new ThreadPoolExecutor.DiscardOldestPolicy()      	// When the queue is full, try to compete with the earliest one. If the competition is successful, execute it, otherwise discard it. Will not abandon exceptions 
4. How to define the maximum thread size of thread pool

1. For CPU intensive CPU with several cores, what is the maximum thread

// Gets the number of cores of the CPU
System.out.println(Runtime.getRuntime().availableProcessors());

2. IO intensive determines the IO consuming threads in your program

(understand CPU intensive and IO intensive)

12. Four functional interfaces

Must master: lambda expression, chain programming, functional interface, Stream flow calculation.

Functional interface: an interface with only one method. As long as it is a functional expression, you can use lambda expression to simplify

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
// Foreach (functional interface of consumer class)

1,Function

Functional interface

/**
 * @desc
 * @auth llp
 * @date 2022 16:43, January 26
 */
public class Demo01 {
    public static void main(String[] args) {
        // Tool class, output the input value
//        Function function = new Function<String, String>() {
//            @Override
//            public String apply(String o) {
//                return o;
//            }
//        };
        Function function = (Function<String, String>) o -> o;

//      Function<String, String> function = (str)->{return str;};

        System.out.println(function.apply("test"));
    }
}
2,Predicate

Assertive interface: there is only one input parameter, and the return value can only be Boolean!

/**
 * @desc
 * @auth llp
 * @date 2022 January 26, 2016 16:54
 */
public class Demo02 {
    public static void main(String[] args) {
        // Judge whether the string is empty
//        Predicate predicate = new Predicate<String>() {
//            @Override
//            public boolean test(String str) {
//                return str.isEmpty();
//            }
//        };
        Predicate<String> predicate = String::isEmpty;

        System.out.println(predicate.test("s"));
        System.out.println(predicate.test(""));
    }
}
3,Consumer

Consumer interface

/**
 * @desc
 * @auth llp
 * @date 2022 17:01, January 26
 */
public class Demo03 {
    public static void main(String[] args) {
        // Print string
//        Consumer consumer = new Consumer<String>() {
//            @Override
//            public void accept(String str) {
//                System.out.println(str);
//            }
//        };
        Consumer<String> consumer = System.out::println;
        
        consumer.accept("test");
    }
}
4,Supplier

Supply type interface

/**
 * @desc
 * @auth llp
 * @date 2022 17:06, January 26
 */
public class Demo04 {
    public static void main(String[] args) {
//        Supplier<String> supplier = new Supplier<String>() {
//            @Override
//            public String get() {
//                System.out.println("get()");
//                return "1024";
//            }
//        };
        Supplier<String> supplier = () -> { return "1024"; };
        
        System.out.println(supplier.get());
    }
}

13. Stream flow calculation

What is Stream computing?

In Java 8, a new interface Stream is added, which can perform various very convenient and efficient aggregation operations or bulk data operations on collections through Lambda expressions.

Stream flow calculation: treat the elements to be processed as a stream, which is transmitted in the pipeline and can be processed on the nodes of the pipeline, including filtering, de duplication, sorting, aggregation, etc., and send the results to the next computing node.

/**
 * @desc stream Flow calculation
 * @auth llp
 * @date 2022 January 27, 2014 14:16
 * The title requires that the title can be completed within one minute and can only be realized with one line of code
 * There are 5 users, filtering
 * 1,ID Must be even
 * 2,Age must be over 23
 * 3,User name to uppercase
 * 4,User names are sorted backwards
 * 5,Output only one user
 */
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 21);
        User u2 = new User(2, "b", 22);
        User u3 = new User(3, "c", 23);
        User u4 = new User(4, "d", 24);
        User u5 = new User(6, "e", 25);
        // Collections are stored
        List<User> userList = Arrays.asList(u1, u2, u3, u4, u5);
        // Calculate the delivery flow
        // lambda expression, chain programming, functional interface, Stream flow calculation.
        userList.stream()
                .filter(u -> u.getId() % 2 == 0)
                .filter(u -> u.getAge() > 23)
                .map(u -> u.getName().toUpperCase())
                .sorted(Comparator.reverseOrder())
                .limit(1)
                .forEach(System.out::println);
    }
}

14,ForkJoin

What is ForkJoin?

Java 1.7 introduces a new concurrency framework Fork/Join Framework, which is mainly used to implement the "divide and conquer" algorithm, especially the recursive functions called after divide and conquer.

The essence of ForkJoin framework is a framework for parallel execution of tasks. It can divide a large task into several small tasks, and finally summarize the results of each small task to obtain the calculation results of the large task. In Java, the ForkJoin framework coexists with ThreadPool, not to replace ThreadPool

In fact, the parallel stream computing introduced in Java 8 is internally implemented by ForkJoinPool.

ForkJoin is in jdk1 7. Execute tasks in parallel! Improve efficiency and use it when there is a large amount of data!

ForkJoin features: job theft

After thread B executes, it steals part of the task execution of thread A, which maintains A double ended queue.

How to use ForkJoin

1. Execute through ForkJoinPool

2. Computing task

Use forkjoinpool Execute (forkjointask <? > task) has no return value

public void execute(ForkJoinTask<?> task)

Schedule (asynchronously) the execution of a given task.

  • parameter

    Task - task

  • abnormal

    NullPointerException - if the task is empty

    RejectedExecutionException if the task cannot be executed as planned

Use forkjoinpool Submit (forkjointask <? > task) has a return value

public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)

Submit forkjointask for execution.

  • Parameter type

    T - task type results

  • parameter

    Task - task submission

  • result

    task

  • abnormal

    NullPointerException - if the task is empty

    RejectedExecutionException if the task cannot be executed as planned

3. The calculation class should inherit ForkJoinTask

  • A typical example is the task of calculating Fibonacci sequence:

     class Fibonacci extends RecursiveTask<Integer> {
       final int n;
       Fibonacci(int n) { this.n = n; }
       Integer compute() {
         if (n <= 1)
           return n;
         Fibonacci f1 = new Fibonacci(n - 1);
         f1.fork();
         Fibonacci f2 = new Fibonacci(n - 2);
         return f2.compute() + f1.join();
       }
     }
    

Test code:

/**
 * @desc Summation task
 * @auth llp
 * @date 2022 January 27, 2015 15:26
 */
public class ForkJoinDemo extends RecursiveTask<Long> {

    private long start;
    private long end;

    // critical value
    private long temp = 10000L;


    public ForkJoinDemo(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if ((end - start) < temp){
            long sum = 0L;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }else {
            // ForkJoin recursion
            long mid = (end + start) / 2;   // Intermediate value
            ForkJoinDemo forkJoinTask1 = new ForkJoinDemo(start, mid);
            forkJoinTask1.fork();       // Split the task and push the task into the thread queue
            ForkJoinDemo forkJoinTask2 = new ForkJoinDemo(mid+1, end);
            forkJoinTask2.fork();       // Split the task and push the task into the thread queue

            return forkJoinTask1.join() + forkJoinTask2.join();
        }
    }
}
/**
 * @desc 3000   6000(ForkJoin)    9000(Stream)
 * @auth llp
 * @date 2022 January 27, 2015 15:59
 */
public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // test1();    // 333
        // test2();    // 198
        test3();       // 157
    }

    // Ordinary programmer
    public static void test1(){
        long start = System.currentTimeMillis();
        long sum = 0L;
        for (int i = 1; i <= 10_0000_0000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum=" + sum + " time: " + (end-start));
    }

    // Can use ForkJoin
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(1L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);  // Submit task
        long sum = submit.get();    // Blocking waiting

        long end = System.currentTimeMillis();
        System.out.println("sum=" + sum + " time: " + (end-start));
    }

    // Stream parallel stream
    public static void test3(){
        long start = System.currentTimeMillis();

        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);

        long end = System.currentTimeMillis();
        System.out.println("sum=" + sum + " time: " + (end-start));
    }
}

15. Asynchronous callback

Future < T > implementation class = = > java util. concurrent Class CompletableFuture<T>

Code test:

/**
 * @desc Asynchronous call
 * @auth llp
 * @date 2022 January 27, 2016 16:29
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // Asynchronous callback with no return value
//        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
//            try {
//                TimeUnit.SECONDS.sleep(2);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
//        });
//        System.out.println("11111111");
//        completableFuture.get();    //  Get blocking execution results

        // Asynchronous callback with return value
        // ajax success and failure have callbacks
        // The error message is returned
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName() + "supplyAsync==>Integer");
            // int i = 10/0;
            return 1024;
        });

        Integer res = completableFuture.whenComplete((T, U) -> {
            System.out.println("T=> " + T);     // Return to normal results
            System.out.println("U=> " + U);     // Return error message: Java util. concurrent. CompletionException: java. lang.ArithmeticException: / by zero
        }).exceptionally((e) -> {
            e.getMessage();
            return 233;     // You can go back to the wrong return result
        }).get();

        System.out.println("Return result:" + res);
    }
}

16,JMM

What is the Java Memory Model? Java Memory Model ( JMM )

Because there are certain differences in memory access logic under different hardware manufacturers and different operating systems, the Java memory model is to shield the differences between systems and hardware, so that a set of code can achieve the same access results under different platforms.

There are 8 kinds of memory interactive operations. The implementation of virtual machine must ensure that each operation is atomic and cannot be separated

  • lock: a variable that acts on main memory and identifies a variable as a thread exclusive state
  • unlock: a variable that acts on the main memory. It releases a locked variable, and the released variable can be locked by other threads
  • read: acts on the main memory variable. It transfers the value of a variable from the main memory to the working memory of the thread for subsequent load actions
  • load: a variable that acts on working memory. It puts the read operation from main memory into working memory
  • Use: it acts on the variables in working memory. It transfers the variables in working memory to the execution engine. Whenever the virtual machine encounters a value that needs to be used, it will use this instruction
  • assign: acts on a variable in working memory. It puts a value received from the execution engine into the variable copy in working memory
  • store: a variable that acts on main memory. It transfers the value of a variable from working memory to main memory for subsequent write
  • write: acts on the variable in main memory. It puts the value of the variable obtained by store operation from working memory into the variable in main memory

JMM formulates the following rules for the use of these eight instructions:

  • One of the read and load, store and write operations is not allowed to appear separately. That is, read must be loaded and store must be written (used in pairs)
  • The thread is not allowed to discard its recent assign operation, that is, after the data of the working variable changes, it must inform the main memory (the main memory must be notified in time of the change of the working memory variable)
  • A thread is not allowed to synchronize data without assign from working memory back to main memory ()
  • A new variable must be born in main memory. Working memory is not allowed to directly use an uninitialized variable. That is, before the use and store operations on variables, they must go through the assign and load operations
  • Only one thread can lock a variable at a time. After multiple locks, you must perform the same number of unlocks to unlock
  • If you lock a variable, it will clear the value of this variable in all working memory. Before the execution engine uses this variable, you must re load or assign to initialize the value of the variable
  • If a variable is not locked, it cannot be unlocked. You cannot unlock a variable that is locked by another thread
  • Before unlock ing a variable, it must be synchronized back to main memory

Some synchronization conventions of JMM:

1. Before the thread is unlocked, the shared variable must be flushed back to main memory immediately.

2. Before locking the thread, you must read the latest value in the main memory into the working memory!

3. Locking and unlocking are the same lock

JMM model features

Atomicity: for example, the above eight operations are inseparable units in the operating system. Operations wrapped in synchronized keywords or other locks can also be considered atomic.

Visibility: each working thread has its own working memory, so when a thread modifies a variable, it may not be observed that the variable has been modified in other threads. Volatile keyword requires that the modified variable be updated to the main memory immediately and read from the main memory before each use. Therefore, volatile ensures visibility. synchronized and final also provide visibility.

Orderliness: the orderliness of java is related to threads. If you look inside the thread, you will find that all operations of the current thread are orderly. If you look outside the thread, you will find that all operations of the thread are out of order.

Code test:

/**
 * @desc
 * @auth llp
 * @date 2022 January 27, 2017 17:55
 */
public class JMMTest {
    private static int num = 0;
    public static void main(String[] args) {
        new Thread(()->{
            while (num == 0){	// Thread A does not know the change of main memory

            }
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num = 1;
        System.out.println(num);
    }
}

Problem: the main thread has modified num, but thread A cannot be seen in time

Problem: the program does not know that the value in memory has been modified. How to ensure visibility? = = > Volatile.

17,Volatile

volatile is a keyword in the Java language that modifies variables.

The variable modified by volatile indicates that the variable is shared in different threads. The compiler and runtime will notice that the variable is shared, so the variable will not be reordered.

volatile is a lightweight synchronization mechanism provided by java virtual machine

1. Ensure visibility (variables are shared in different threads)
/**
 * @desc
 * @auth llp
 * @date 2022 January 27, 2017 17:55
 */
public class JMMTest {
    // Without volatile, the program will loop
    private volatile static int num = 0;
    public static void main(String[] args) {
        new Thread(()->{    // Thread A does not know the change of main memory
            while (num == 0){

            }
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num = 1;
        System.out.println(num);
    }
}
2. Atomicity is not guaranteed

Thread A cannot be disturbed or divided when executing tasks. Either succeed at the same time or fail at the same time.

/**
 * @desc Atomicity is not guaranteed
 * @auth llp
 * @date 2022 January 28, 2009 9:24
 */
public class VolatileDemo {
    // volatile does not guarantee atomicity
    // private volatile static int num = 0;
    private static int num = 0;

    public static void add(){
        num++;
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }, String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2){   // main GC
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

num + + is not an atomic operation at all:

How to ensure atomicity without lock and synchronized?

Use atomic classes to solve atomicity problems:

/**
 * @desc Atomicity is not guaranteed
 * @auth llp
 * @date 2022 January 28, 2009 9:24
 */
public class VolatileDemo {

    // volatile does not guarantee atomicity
    // private volatile static int num = 0;
    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add(){
        // num++;  //  Not an atomic operation
        num.getAndIncrement();      // Getandincrement() + 1 method
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }, String.valueOf(i)).start();
        }

        while (Thread.activeCount() > 2){   // main GC
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

The bottom layer of these classes uses native methods, which are directly linked to the operating system and modified directly in memory. UnSafe class is a very special existence.

3. Prohibit instruction rearrangement (add memory barrier)

18. Singleton mode

Hungry man style. I'm very hungry. I'll create it as soon as I come in.

/**
 * @desc Starving singleton mode may waste space
 * @auth llp
 * @date 2022 January 28, 2009 9:52
 */
public class Hungry {

    // Constructor private
    private Hungry(){}

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }
}

Lazy style. It is created only when you are lazy to use it.

/**
 * @desc Lazy singleton mode
 * @auth llp
 * @date 2022 January 28, 2009 9:55
 */
public class LazyMan {

    private LazyMan(){
        System.out.println(Thread.currentThread().getName() + " ok");
    }

    private static LazyMan lazyMan;

    // There is no problem under single thread, and there is a problem under multi thread
    public static LazyMan getInstance(){
        if (lazyMan == null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }

    // Thread testing
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}

Lazy single instance DCL mode of double detection lock mode:

/**
 * @desc Lazy singleton mode
 * @auth llp
 * @date 2022 January 28, 2009 9:55
 */
public class LazyMan {

    private LazyMan(){
        System.out.println(Thread.currentThread().getName() + " ok");
    }
	// Therefore, volatile must be added here to avoid instruction rearrangement
    private volatile static LazyMan lazyMan;

    // Lazy single instance DCL mode with double detection lock mode
    public static LazyMan getInstance(){
        if (lazyMan == null){
            synchronized (LazyMan.class){	// Lock class
                if (lazyMan == null){
                    lazyMan = new LazyMan();    // It is not an atomic operation, and instruction rearrangement may occur
                    // 1. Allocate memory space
                    // 2. Execute the construction method and initialize the object
                    // 3. Point this object to this space
                }
            }
        }
        return lazyMan;	// It is possible that the structure has not been completed
    }
}

Static inner class:

/**
 * @desc Static inner class
 * @auth llp
 * @date 2022 January 28, 2010 10:07
 */
public class Holder {

    public Holder() {
    }

    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}

Destroy lazy singleton DCL mode singleton mode:

/**
 * @desc Lazy singleton mode
 * @auth llp
 * @date 2022 January 28, 2009 9:55
 */
public class LazyMan {

    private static boolean mainbao = false;

    private LazyMan(){
        synchronized (LazyMan.class){
            if (mainbao == false){
                mainbao = true;
            }else {
                throw new RuntimeException("Do not use reflection breaking exceptions");
            }
//            if (lazyMan != null){
//                throw new RuntimeException("do not use reflection breaking exception");
//            }
        }
    }

    // Therefore, volatile must be added here to avoid instruction rearrangement
    private volatile static LazyMan lazyMan;

    // Lazy single instance DCL mode with double detection lock mode
    public static LazyMan getInstance(){
        if (lazyMan == null){
            synchronized (LazyMan.class){
                if (lazyMan == null){
                    lazyMan = new LazyMan();    // It is not an atomic operation, and instruction rearrangement may occur
                    // 1. Allocate memory space
                    // 2. Execute the construction method and initialize the object
                    // 3. Point this object to this space
                }
            }
        }
        return lazyMan; // It is possible that the structure has not been completed
    }

    // Destruction by reflection
    public static void main(String[] args) throws Exception {
        // LazyMan instance = LazyMan.getInstance();    //  Constructor lock judgment throw exception
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance1 = declaredConstructor.newInstance();
        LazyMan instance2 = declaredConstructor.newInstance();  // Both are created using reflection mechanism, and it is useless to judge whether the constructor locks = = > flag variables are used

        // System.out.println(instance);
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

How to prevent reflection from damaging a singleton, click to view declaredconstructor Newinstance () source code:

Use enumeration class singleton mode:

public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws Exception {   // Cannot reflectively create enum objects
        EnumSingle enumSingle1 = EnumSingle.INSTANCE;
        EnumSingle enumSingle2 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);// Use the decompile tool to see that it is a parametric structure
        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle3 = declaredConstructor.newInstance();

        System.out.println(enumSingle1);
        System.out.println(enumSingle2);
        System.out.println(enumSingle3);
    }
}

19,CAS (compare and swap)

What is CAS?

CAS is the abbreviation of compare and swap, which is what we call comparative exchange. CAS is based on optimistic locks. Pessimistic locks lock resources. Only after a thread that has previously obtained the lock releases the lock can the next thread access it. The optimistic lock handles resources without locking in some way, such as adding version to the record to obtain data. The performance is much better than the pessimistic lock.

Generally speaking, optimistic lock has the following two ways:

  • It is implemented using the data version recording mechanism, which is the most commonly used implementation of optimistic locking. What is data version? That is, adding a version ID to the data is generally realized by adding a numeric "version" field to the database table. When reading data, read out the value of the version field together. Add one to the version value every time the data is updated. When we submit the update, we judge that the current version information of the corresponding record of the database table is compared with the version value obtained for the first time. If the current version number of the database table is equal to the version value obtained for the first time, it will be updated, otherwise it will be considered as expired data.
  • Use a timestamp. The second implementation of optimistic locking is similar to the first. It also adds a field in the table that needs optimistic lock control. The name doesn't matter. The field type uses a timestamp. Similar to the above version, it also checks the timestamp of the data in the current database when the update is submitted and compares it with the timestamp obtained before the update, If it is consistent, it is OK. Otherwise, it is a version conflict.

If the expected value is met, it will be updated. Otherwise, it will not be updated. CAS is the CPU concurrency primitive.

CAS: compare the value in the current working memory. If this value is expected, execute the operation! If not, it will cycle all the time (the bottom layer is spin lock).

Unsafe class

Spin lock:

Disadvantages: ABA problem

Thread B performs two operations, but thread A does not know the process and thinks that A is still the original A.

/**
 * @desc
 * @auth llp
 * @date 2022 January 28, 2010 10:39
 */
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2022);

        // public final boolean compareAndSet(int expect, int update)
        // Expectation, update
        // ======================Troublemaker thread===========================
        System.out.println(atomicInteger.compareAndSet(2022, 2023));
        System.out.println(atomicInteger.get());

        System.out.println(atomicInteger.compareAndSet(2023, 2022));
        System.out.println(atomicInteger.get());

        // =====================Expected thread===========================
        System.out.println(atomicInteger.compareAndSet(2022, 2025));
        System.out.println(atomicInteger.get());

    }
}

20. Atomic reference

The default range of Integer object caching mechanism is - 128 ~ 127. It is recommended to use the static factory method valueOf to obtain object instances instead of new. Because valueOf uses Oh caching mechanism, new objects will be created and new memory space will be allocated for new.

Solve ABA problem: introduce atomic reference. The principle of optimistic lock is the same, and the version number is added.

/**
 * @desc
 * @auth llp
 * @date 2022 January 28, 2010 10:39
 */
public class CASDemo {
    public static void main(String[] args) {

        AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(20, 1);

        new Thread(()->{
            int stamp = stampedReference.getStamp();    // Get version number
            System.out.println("A1==>" + stamp);

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(stampedReference.compareAndSet(20, 22,
                    stampedReference.getStamp(), stampedReference.getStamp() + 1));

            System.out.println("A2==>" + stampedReference.getStamp());

            System.out.println(stampedReference.compareAndSet(22, 20,
                    stampedReference.getStamp(), stampedReference.getStamp() + 1));

            System.out.println("A2==>" + stampedReference.getStamp());
        }, "A").start();

        new Thread(()->{
            int stamp = stampedReference.getStamp();    // Get version number
            System.out.println("B1==>" + stamp);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(stampedReference.compareAndSet(20, 66, stamp, stamp + 1));

            System.out.println("B2==>" + stampedReference.getStamp());

        }, "B").start();
    }
}

21. Various locks

1. Fair lock, unfair lock
public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
2. Reentrant lock

Reentrant lock (recursive lock)

/**
 * @desc
 * @auth llp
 * @date 2022 January 28, 2014 14:42
 */
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(phone::send, "A").start();
        new Thread(phone::send, "B").start();
    }
}
class Phone{
    public synchronized void send(){
        System.out.println(Thread.currentThread().getName() + "send");
        call(); // There's a lock here
    }
    public synchronized void call(){
        System.out.println(Thread.currentThread().getName() + "call");
    }
}
/**
 * @desc
 * @auth llp
 * @date 2022 January 28, 2014 14:45
 */
public class Demo02 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        new Thread(phone::send, "A").start();
        new Thread(phone::send, "B").start();
    }
}
class Phone2{
    Lock lock = new ReentrantLock();
    public void send(){
        lock.lock();    // Details: Lock send - > lock call - > unlock call - > unlock send
        try {
            System.out.println(Thread.currentThread().getName() + "send");
            call(); // There's a lock here
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void call(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
3. Spin lock

Custom Lock test:

/**
 * @desc Spin lock
 * @auth llp
 * @date 2022 January 28, 2014 14:51
 */
public class SpinlockDemo {
    // int default 0
    // The default value of Thread is null
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    // Lock
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName() + "==> myLock");

        // Spin lock
        while (!atomicReference.compareAndSet(null, thread)){

        }
    }
    // Unlock
    public void myUnlock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName() + "==> myUnlock");
        atomicReference.compareAndSet(thread, null);
    }
}

class Test{
    public static void main(String[] args) throws InterruptedException {
//        ReentrantLock reentrantLock = new ReentrantLock();
//        reentrantLock.lock();
//        reentrantLock.unlock();

        SpinlockDemo spinlockDemo = new SpinlockDemo();

        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        }, "T1").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            spinlockDemo.myLock();      // T2 goes in and the lock is obtained by T1. You can only spin and wait for T1 to release the lock
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        }, "T2").start();

    }
}
4. Deadlock

(mirror lipstick example)

Deadlock refers to the phenomenon that two or more processes wait for each other due to competing for resources in the implementation process. If there is no external force, they will not be able to move forward.

(multiple threads hold each other's required resources, and then form a stalemate)

The occurrence of deadlock must meet the following four conditions at the same time:

  • Mutually exclusive condition: a resource can only be used by one process at a time.
  • Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources.
  • Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived until they are used up at the end of the year.
  • Circular waiting condition: a circular waiting resource relationship is formed between several processes.

How to prevent deadlock?

  • Lock sequence (threads work in sequence)
  • Lock time limit (when the thread requests the permission, it will give up when it times out, and release the lock it owns at the same time)
  • Deadlock detection

solve the problem

1. Use jps -l to locate the process number.

2. Use jstack - process number to find the deadlock problem.

Keywords: Java Concurrent Programming JUC

Added by Arrow on Sun, 30 Jan 2022 00:05:35 +0200