Basics of JUC 【Thousands of Words】

players

1. What is JUC?

image-20210828155426978

JUC: Refers to the three concurrent programming toolkits of java.util

  1. java.util.concurrent
  2. java.util.concurrent.atomic
  3. java.util.concurrent.locks

Four ways to achieve multithreading:

  1. Inherit the Thread class
  2. Implement the Runnable interface
  3. Implement the Callable interface
  4. Thread Pool

Business: common thread code Thread

Runnable has no return value, and its efficiency is relatively low compared to Callable!

Difference between Runnable interface and Callable interface:

  1. Whether there is a return value: Runnable has no return value, and Callable has a return value
  2. Whether to throw an exception: the call method calculates a result, and if it cannot do so, an exception is thrown
  3. The implementation method names are different, the Runnable interface is the run method, and the Callable interface is the call method

2. Threads and processes

Process: refers to an application program that is running in the system; once the program is running, it is a process; process - the smallest unit of resource allocation.

Thread: The basic unit that the system allocates processor time resources, or a unit execution flow that is independently executed within a process. Thread - The smallest unit of program execution.

Process: It is a program, a process contains multiple threads, and contains at least one thread.

Java has two threads by default: main and GC.

Can Java open threads? The start method starts the thread

 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 */
            }
        }
    }

 //本地方法,底层C++,Java无法操作硬件,由操作系统决定是否创建线程,是否立即创建线程
 private native void start0();

Java cannot start threads . The underlying method is to call start0(), which is a native method written by the underlying C++ method. Java cannot directly manipulate hardware.

Several states of the thread

Thread.State

 public enum State {
    
    
        NEW,//新建     
        RUNNABLE,//准备就绪       
        BLOCKED, //阻塞        
        WAITING,//一直等待
        TIMED_WAITING,//超时等待,过时不候
        TERMINATED;//终止
    }

wait/sleep difference

1. From a different class

wait => Object, any object instance can call

sleep => Thread, the static method of Thread

2. About the release of the lock

wait will release the lock; sleep will not release the lock, and it does not need to occupy the lock

3. The scope of use and exception capture are different

wait: must be used in a synchronous code block, no exceptions need to be caught

sleep: can be used anywhere , exceptions must be caught

concurrent, parallel

Concurrent programming: concurrent, parallel

Concurrency: Multiple threads access the same resource at the same time (multi-threaded shared resources)

​ For example: grabbing tickets for Spring Festival travel, e-commerce flash sales

Parallelism: Multiple tasks are performed together and aggregated later

​ For example: make instant noodles, while the electric kettle is boiling water, disassemble the instant noodles seasoning and pour it into the bucket

 System.out.println(Runtime.getRuntime().availableProcessors());//获取cpu的核数

The essence of concurrent programming: make full use of CPU resources

Monitor

Monitor monitor (that is, the usual lock)

It is a synchronization mechanism that ensures that only one thread accesses the protected data or code at the same time

Jvm synchronization is based on entry and exit, implemented using monitor objects

User threads and daemon threads

User thread: custom thread (new Thread())

daemon thread: a special thread in the background, such as garbage collection

image-20220928232336843

The main thread is over, the user thread is still running, and the jvm survives

image-20220928232600747

There are no user threads anymore, they are all daemon threads, and jvm ends

3. Lock lock (emphasis)

Multithreaded programming steps:

  1. Create a resource class, create attributes and operation methods in the resource class
  2. Create multiple threads and call the operation method of the resource class

Case: Three conductors sell 30 tickets at the same time.

traditional synchronized

public class SaleTicketDemo1 {
    
    
    public static void main(String[] args) {
    
    
        //并发:多个线程操作同一个资源类,把资源类丢入线程
        Ticket ticket = new Ticket();

        //Runnable接口 -》 函数式接口,lambda表达式:函数式接口的实例
        new Thread(() -> {
    
    
            for (int i = 0; i < 30; i++) {
    
    
                ticket.sale();
            }
        },"A").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 30; i++) {
    
    
                ticket.sale();
            }
        },"B").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 30; i++) {
    
    
                ticket.sale();
            }
        },"C").start();

    }
}

/**
 * 资源类
 */
class Ticket {
    
    
    //属性
    private int num = 30;

    //synchronized 本质:线程串行化,排队,锁
    public synchronized void sale() {
    
    
        if (num > 0) {
    
    
            System.out.println(Thread.currentThread().getName() + "卖出了第" + (num--) + "张票,剩余" + num + "张");
        }
    }

}

Lock interface

image-20210828171909681

Implementation class

image-20210828172055385

image-20210828172743204

Fair lock: very fair, first come, first served (sunshine, relatively low efficiency)

Unfair lock: very unfair, you can jump in the queue (by default, it may cause thread starvation, but the efficiency is high)

public class SaleTicketDemo2 {
    
    
    public static void main(String[] args) {
    
    
        //并发:多个线程操作同一个资源类,把资源类丢入线程
        Ticket2 ticket = new Ticket2();

        new Thread(() -> {
    
    for (int i = 0; i < 40; i++) ticket.sale();},"A").start();
        new Thread(() -> {
    
    for (int i = 0; i < 40; i++) ticket.sale();},"B").start();
        new Thread(() -> {
    
    for (int i = 0; i < 40; i++) ticket.sale();},"C").start();
    }
}

/**
 * 资源类
 *
 * Lock三部曲
 * 1.new ReentrantLock();
 * 2.lock.lock(); //加锁
 * 3.finally  -》 lock.unlock();//解锁
 *
 */
class Ticket2 {
    
    
    //属性
    private int num = 30;

    // 创建可重入锁
    Lock lock = new ReentrantLock();

    public void sale() {
    
    

        lock.lock(); //加锁

        try {
    
    
            //业务代码
            if (num > 0) {
    
    
                System.out.println(Thread.currentThread().getName() + "卖出了第" + (num--) + "张票,剩余" + num + "张");
            }
        } finally {
    
    
          lock.unlock();//解锁
        }
    }

}

The difference between synchronized and Lock

1. Synchronized is a built-in Java keyword, and Lock is an interface of Java

2. Synchronized cannot determine the status of the lock acquisition, but Lock can determine whether the lock has been acquired

3. Synchronized will automatically release the lock, Lock must release the lock manually! If the lock is not released, it will cause a deadlock

4. Once the synchronized thread is blocked while acquiring the lock, the second thread can only wait foolishly; the Lock will not necessarily wait

5. Synchronized reentrant lock, non-interruptible, unfair; Lock, reentrant lock, can judge lock, unfair/fair (can be set by yourself, default unfair lock)

6. synchronized is suitable for locking a small amount of synchronization code; Lock is suitable for locking a large amount of synchronization code!

7. Lock can improve the efficiency of multiple threads for read operations.

在性能上来说,如果竞争资源不激烈,两者性能是差不多的,而当竞争资源非常激烈时(即大量线程同时竞争),此时Lock的性能要远远优于synchronized

4. Communication between threads

Producer and Consumer Problems

Interview: singleton mode, sorting algorithm, producer consumer problem, deadlock

Resource class operation steps: Judgment Waiting-"Executing Business-"Wake Up Notification

Producer and consumer problem synchronized version

Object类中方法

image-20221002231551301

/**
 * 线程之间通信问题:生产者和消费者问题!  等待唤醒,通知唤醒
 * 线程之间交替执行 A B 操作同一个变量 num = 0
 * A num+1
 * B num-1
 */
public class ProducerAndConsumer {
    
    
    public static void main(String[] args) {
    
    
        Data data = new Data();

        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.decrement();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}

/**
 * 资源类
 *
 * 判断等待,业务,通知
 */
class Data {
    
    
    private int num = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
    
    
        if (num != 0) {
    
    
            //等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        //通知其他线程我+1 完毕了
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
    
    
        if (num == 0) {
    
    
            //等待
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        //通知其他线程我-1 完毕了
        this.notifyAll();
    }
}

image-20221002231247476

The problem exists, ABCD four threads spurious wake-up

image-20210828182933101

public class ProducerAndConsumer {
    
    
    public static void main(String[] args) {
    
    
        Data data = new Data();

        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.decrement();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.decrement();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

/**
 * 资源类
 * <p>
 * 判断等待,业务,通知
 */
class Data {
    
    

    private int num = 0;

    /**
     * +1
     */
    public synchronized void increment() throws InterruptedException {
    
    
        if (num != 0) {
    
    
            //等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        //通知其他线程我+1 完毕了
        this.notifyAll();
    }

    /**
     * -1
     */
    public synchronized void decrement() throws InterruptedException {
    
    
        if (num == 0) {
    
    
            //等待
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        //通知其他线程我-1 完毕了
        this.notifyAll();
    }
}

image-20221002233855349

为什么会出现虚假唤醒问题呢?

Mainly because the wait method wakes up from where it waits (and wait will not hold the lock, other threads can get the lock directly), if will only judge once, as long as the condition is met for the first time, wait for wakeup The following +1/-1 operations will be executed directly, so there will be >1 or <0 numbers or even deadlocks, which are caused by false wakeups.

Prevent false wake-up problems: change if to while

/**
 * 线程之间通信问题:生产者和消费者问题!  等待唤醒,通知唤醒
 * 线程之间交替执行 A B 操作同一个变量 num = 0
 * A num+1
 * B num-1
 */
public class ProducerAndConsumer {
    
    
    public static void main(String[] args) {
    
    
        Data data = new Data();

        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.decrement();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.decrement();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

/**
 * 资源类
 *
 * 判断等待,业务,通知
 */
class Data {
    
    
    private int num = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
    
    
        while (num != 0) {
    
    
            //等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        //通知其他线程我+1 完毕了
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
    
    
        while (num == 0) {
    
    
            //等待
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "=>" + num);
        //通知其他线程我-1 完毕了
        this.notifyAll();
    }
}

The JUC version of the producer and consumer problem

Find Condition by Lock (official document)image-20210828183902401

Code

public class ProducerAndConsumer2 {
    
    

    public static void main(String[] args) {
    
    
        Data2 data = new Data2();

        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.decrement();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.increment();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                try {
    
    
                    data.decrement();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }, "D").start();

    }
}

/**
 * 资源类
 *
 * 判断等待,业务,通知
 */
class Data2 {
    
    
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    //condition.await(); // 等待
    //condition.signalAll(); // 唤醒全部

    //+1
    public void increment() throws InterruptedException {
    
    
        lock.lock();

        try {
    
    
            //业务代码
            while (num != 0) {
    
    
                //等待
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName() + "=>" + num);
            //通知其他线程我+1 完毕了
            condition.signalAll();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }

    }

    //-1
    public void decrement() throws InterruptedException {
    
    
        lock.lock();
        try {
    
    
            while (num == 0) {
    
    
                //等待
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName() + "=>" + num);
            //通知其他线程我-1 完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
}

Complete steps of multithreaded programming

Step 1: Create a resource class, create attributes and operation methods in the resource class

Step 2: Resource class operation method

(1) Judgment waiting

(2) Business logic

(3) Notification wake-up

Step 3: Create multiple threads and call the operation method of the resource class

Step Four: Prevent False Wakeups

线程间定制通信

Threads execute in agreed order

Any new technology definitely does not just cover the original technology, it has its advantages and supplements!

Condition precise notification and wake-up thread

image-20210828191750451

Code implementation: three threads execute sequentially

/**
 * A B C三个线程顺序执行
 */
public class C {
    
    
    public static void main(String[] args) {
    
    
        Resource resource = new Resource();

        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                resource.printA();
            }
        }, "A").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                resource.printB();
            }
        }, "B").start();
        new Thread(() -> {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                resource.printC();
            }
        }, "C").start();
    }
}

/**
 * 资源类 Lock
 */
class Resource {
    
    
    private final Lock lock = new ReentrantLock();
    private final Condition condition1 = lock.newCondition();
    private final Condition condition2 = lock.newCondition();
    private final Condition condition3 = lock.newCondition();
    private int num = 1; // 1A 2B 3C


    public void printA() {
    
    
        lock.lock();
        try {
    
    
            //业务 判断 -》 执行 -》 通知
            while (num != 1) {
    
    
                //等待
                condition1.await();
            }
            num = 2;
            System.out.println(Thread.currentThread().getName() + "=>AAA");
            //唤醒,唤醒指定线程B
            condition2.signal();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }

    public void printB() {
    
    
        lock.lock();
        try {
    
    
            //业务 判断 -》 执行 -》 通知
            while (num != 2) {
    
    
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>BBB");
            num = 3;
            //唤醒线程C
            condition3.signal();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }

    }

    public void printC() {
    
    
        lock.lock();
        try {
    
    
            //业务 判断 -》 执行 -》 通知
            while (num != 3) {
    
    
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>CCC");
            num = 1;
            //唤醒线程A
            condition1.signal();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }

    }

}

5. Eight-lock phenomenon

What is a lock? How to tell who the lock is?

object of any class, class object

Deep understanding of locks

 * 八锁,就是关于锁的八个问题
 * 1、标准情况下,两个线程先打印 发短信 还是 打电话? 1/发短信 2/打电话
 * 2sendSms()延迟4s情况下,两个线程先打印 发短信 还是 打电话? 1/发短信 2/打电话
 */
public class Test1 {
    
    
    public static void main(String[] args) {
    
    
        Phone phone = new Phone();

        //锁的存在
        new Thread(() -> {
    
    
            phone.sendSms();

            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        },"A").start();

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

class Phone{
    
    

    //synchronized 锁的对象是方法的调用者!
    //两个方法用的是同一把锁,谁先拿到谁执行!
    public synchronized void sendSms(){
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
    
    
        System.out.println("打电话");
    }
}
/**
 * 3、增加了一个普通方法后,先打印发短信还是hello? hello
 * 4、两个对象,两个同步方法 两个线程先打印 发短信 还是 打电话? 1/打电话 2/发短信
 */
public class Test2{
    
    
    public static void main(String[] args) {
    
    

        //两个对象,两个不同的对象,两把锁!
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        //锁的存在
        new Thread(() -> {
    
    
            phone1.sendSms();

            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        },"A").start();

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

class Phone2{
    
    

    //synchronized 锁的对象是方法的调用者!
    //两个方法用的是同一把锁,谁先拿到谁执行!
    public synchronized void sendSms(){
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
    
    
        System.out.println("打电话");
    }

    //没有锁,不是同步方法,不受锁的影响
    public void hello(){
    
    
        System.out.println("hello");
    }
}
/**
 * 5.增加两个静态的同步方法 一个对象 两个线程先打印 发短信 还是 打电话? 1/发短信 2/打电话
 * 6.两个对象 两个静态的同步方法 两个线程先打印 发短信 还是 打电话?    1/发短信 2/打电话
 */
public class Test3 {
    
    
    public static void main(String[] args) {
    
    

        //两个不同的对象 但是类只会加载一次 共用一把锁
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        //锁的存在
        new Thread(() -> {
    
    
            phone1.sendSms();

            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        },"A").start();

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

class Phone3{
    
    

    //static静态方法 锁的是类对象 Phone3.class
    public static synchronized void sendSms(){
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
    
    
        System.out.println("打电话");
    }
}
/**
 * 7.一个静态同步方法一个普通同步方法 一个对象 两个线程先打印 发短信 还是 打电话? 打电话 发短信
 * 8.一个静态同步方法一个普通同步方法 两个对象 两个线程先打印 发短信 还是 打电话? 打电话 发短信
 */
public class Test4 {
    
    
    public static void main(String[] args) {
    
    

        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();

        //锁的存在
        new Thread(() -> {
    
    
            phone1.sendSms();

            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        },"A").start();

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

class Phone4{
    
    

    //static静态同步方法 锁的是类对象 Phone3.class
    public static synchronized void sendSms(){
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    //普通同步方法 锁的是方法调用者
    public synchronized void call(){
    
    
        System.out.println("打电话");
    }

}

summary:

  • In the synchronization method, whoever gets the lock first executes first, and the same lock is executed sequentially.
  • The ordinary synchronization method locks the current object of this (that is, the caller of the method).
  • The static synchronization method locks the class object, and the class will only be loaded once, so the static synchronization method always locks the class object (XXX.class).

The basis of synchronized implementation of synchronization: every object in Java can be used as a lock.

It is manifested in the following three forms.

  1. For normal synchronized methods, the lock is the current instance object.
  2. For static synchronized methods, the lock is the Class object of the current class.
  3. For a synchronized method block, the lock is the object configured in the synchronized brackets.

6. The collection class is not safe

List is not safe

//java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
    
    
    public static void main(String[] args) {
    
    

        //并发下 ArrayList不安全
        /*
        解决方案
        1、List<String> list = new Vector<>();
        2、List<String> list = Collections.synchronizedList(new ArrayList<>());
        3、List<String> list = new CopyOnWriteArrayList<>();
         */

        //CopyOnWrite 写入时复制  COW 计算机程序设计领域的一种优化策略;
        //多个线程操作的时候 list 读取的时候 固定的 写入(覆盖)
        //再写入的时候避免覆盖,造成数据问题!
        //读写分离
        //CopyOnWriteArrayList 比 Vector 好在哪里?
        //Vector 底层是synchronized实现效率较低 ; CopyOnWriteArrayList 底层是ReentrantLock实现 效率更高 灵活性也更高

        List<String> list = new CopyOnWriteArrayList<>();
        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

import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

//java.util.ConcurrentModificationException
public class SetTest {
    
    
    public static void main(String[] args) {
    
    
        //HashSet<String> set = new HashSet<>();
        //解决方案一:
        //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();
        }
    }
}

What is the bottom layer of HashSet?

The bottom layer of HashSet is HashMap

public HashSet() {
    
    
    map = new HashMap<>();
}
// add()  set 本质就是map 中的key, key是不可重复的!
public boolean add(E e) {
    
    
    return map.put(e, PRESENT)==null;
}

// PRESENT 不变的值!
private static final Object PRESENT = new Object();

Map is not safe

Review the bottom layer of HashMap

image-20210828213421678

public class MapTest {
    
    
    public static void main(String[] args) {
    
    
        /*
        并发下 HashMap线程不安全

        解决方案:
        1.Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());
        2.Map<String, Object> map = new ConcurrentHashMap<>();
         */

        //HashMap<String, Object> map = new HashMap<>();

        //Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, Object> 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

image-20210828214714670

1. Can have a return value

2. Can throw an exception

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

code testing

public class CallableTest {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        new Thread(new MyThread()).start();

        /*
        本身Callable接口和Runnable接口毫无关系
        通过一个Runnable接口的实现类FutureTask,Callable接口与Runnable接口构建了关系,便可以启动线程
         */

        //适配类   FutureTask 是 Runnable接口的实现类   构造器 FutureTask(Callable<V> callable)
        MyThread1 t1 = new MyThread1();
        FutureTask<Integer> futureTask = new FutureTask<>(t1); //泛型是线程返回值类型

        /*
        启动两个线程,只会打印一个`call()...`
         */
        new Thread(futureTask,"A").start(); //怎么启动Callable
        new Thread(futureTask,"B").start(); //结果会被缓存,效率高

        Integer i = futureTask.get(); //获取线程返回值  get()可能会产生阻塞!把他放到最后 或者 使用异步通信来处理!

        System.out.println(i);
    }
}

class MyThread implements Runnable{
    
    

    @Override
    public void run() {
    
    

    }
}

/**
 * 泛型是返回值类型
 */
class MyThread1 implements Callable<Integer>{
    
    

    @Override
    public Integer call(){
    
    
        System.out.println("call()...");
        //耗时的操作
        return 1024;
    }
}

Notice:

1. There is a cache

2. You may need to wait to get the result, it will be blocked!

8. Commonly used auxiliary classes (must)

8.1 CountDownLatch (subtraction counter)

image-20210828222231364

/**
 * 计数器
 */
public class CountDownLatchDemo {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        //倒计时 起始为6 必须要执行任务的时候,再使用!
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 0; i < 6; i++) {
    
    
            new Thread(() -> {
    
    

                System.out.println(Thread.currentThread().getName() + "--->Go out");
                countDownLatch.countDown(); // -1
            }, String.valueOf(i)).start();
        }

        countDownLatch.await(); //等待计数器归零在向下执行

        System.out.println("Close Door!");
    }
}

principle:

countDownLatch.countDown () ; // count - 1

countDownLatch.await () ; // wait for the counter to return to 0, and then execute downward

Every time a thread calls countDown() the number -1, when the counter becomes 0, countDownLatch.await() will be woken up and continue to execute!

8.2 CyclicBarrier (adding counter)

image-20210828224252023

public class CyclicBarrierDemo {
    
    
    public static void main(String[] args) {
    
    
        /*
          集齐7颗龙珠召唤神龙
         */
        //召唤神龙的线程
        CyclicBarrier barrier = new CyclicBarrier(7, () -> System.out.println("成功召唤神龙!"));

        for (int i = 1; i <= 7; i++) {
    
    
            final int temp = i;

            new Thread(() -> {
    
    
                System.out.println(Thread.currentThread().getName() + "收集了" + temp + "个龙珠");

                try {
    
    
                    barrier.await(); //等待
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

8.3 Semaphore (semaphore)

image-20210828225726951

Grab a parking spot!

6 cars - 3 parking spaces

public class SemaphoreDemo {
    
    
    public static void main(String[] args) {
    
    
        //线程数量:停车位! 限流!
        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i <= 6; i++) {
    
    

            new Thread(() -> {
    
    
                try {
    
    
                    //acquire() 获得
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "抢到车位");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName() + "离开车位");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    semaphore.release(); //release() 释放
                }
            },String.valueOf(i)).start();

        }
    }
}

principle:

semaphore.acquire () ; //Acquire, if it is full, wait until it is released!

semaphore.release () ; // release, will release the current semaphore +1, and then wake up the waiting thread!

Role: Mutually exclusive use of multiple shared resources! Concurrent current limiting, control the maximum number of threads!

9. Read-write lock

Write lock: exclusive lock

Read lock: shared lock

Read-write lock: A resource can be accessed by multiple read threads, or by a write thread, but read and write threads cannot exist at the same time, read and write are mutually exclusive, and read and read are shared

ReadWriteLock

image-20210829160712148

public class ReadWriteLockDemo {
    
    
    public static void main(String[] args) {
    
    
        MyCache myCache = new MyCache();

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

        for (int i = 1; i <= 5; i++) {
    
    
            final int temp = i;
            //读取
            new Thread(() -> {
    
    
                System.out.println("获取" + temp + "缓存数据-> " + myCache.get(temp + ""));
            }, String.valueOf(i)).start();
        }
    }
}

/**
 * 自定义缓存
 */
class MyCache {
    
    

    private volatile Map<String, Object> map = new HashMap<>();

    /**
     * 存 写
     */
    public void put(String key, Object value) {
    
    
        System.out.println(Thread.currentThread().getName() + "写入" + key);
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "写入OK");
    }

    /**
     * 取 读
     */
    public Object get(String key) {
    
    
        System.out.println(Thread.currentThread().getName() + "读取" + key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取OK");
        return o;
    }
}

operation result:

image-20221007182311404

Result analysis: Since it is a multi-threaded operation, multiple threads are started at the same time. It may take some time for the writing thread to write data. At this time, the data is not written yet, and the reading thread starts to read the data, resulting in no data being read. This situation requires locking control.

Add read-write lock:

/**
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁)  多个线程可以同时占有
 * 
 * ReadWriteLock
 * 读-读  可以共存!
 * 读-写  不可共存!
 * 写-写  不可共存!
 */
public class ReadWriteLockDemo {
    
    
    public static void main(String[] args) {
    
    
        MyCache myCache = new MyCache();

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

        for (int i = 1; i <= 5; i++) {
    
    
            final int temp = i;
            //读取
            new Thread(() -> {
    
    
                System.out.println("获取" + temp + "缓存数据-> " + myCache.get(temp + ""));
            }, String.valueOf(i)).start();
        }
    }
}

/**
 * 自定义缓存
 */
class MyCache {
    
    

    private volatile Map<String, Object> map = new HashMap<>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * 存 写
     */
    public void put(String key, Object value) {
    
    
        readWriteLock.writeLock().lock();
        System.out.println(Thread.currentThread().getName() + "写入" + key);
        try {
    
    
            TimeUnit.MILLISECONDS.sleep(200);
            map.put(key, value);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            readWriteLock.writeLock().unlock();
        }
        System.out.println(Thread.currentThread().getName() + "写入OK");
    }

    /**
     * 取 读
     */
    public Object get(String key) {
    
    
        readWriteLock.readLock().lock();
        Object o = null;
        try {
    
    
            System.out.println(Thread.currentThread().getName() + "读取" + key);
            o = map.get(key);
            System.out.println(Thread.currentThread().getName() + "读取OK");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            readWriteLock.readLock().unlock();
        }
        return o;
    }
}

At this point the result is correct

image-20221007183216136

Result analysis: Due to the addition of a read-write lock, the write lock is an exclusive lock, and the read lock is a shared lock. Therefore, no operation is allowed during the writing process. After the writing operation is completed, multiple threads can share the reading.

Lock downgrade: Write locks will be downgraded to read locks, and read locks cannot be upgraded to write locks.

10. Blocking queue

BlockingQueue
image-20210829164117604

image-20210829163923267

image-20210829170254467

Under what circumstances will we use blocking queues: multi-threaded concurrent processing, thread pool!

Learn to use queues

add, remove

Four APIs

Way Throw an exception does not throw an exception blocking wait timeout wait
Add to add() offer() put() offer(E e, long timeout, TimeUnit unit)
remove remove() poll() take() poll(long timeout, TimeUnit unit)
Get the first element of the queue element() peek() - -
    /**
     * 抛出异常
     */
    public static void test1() {
    
    
        //队列的大小
        ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));

        //java.lang.IllegalStateException: Queue full 抛出异常
        //System.out.println(arrayBlockingQueue.add("d"));

        System.out.println("**************");
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());

        //java.util.NoSuchElementException 抛出异常
        //System.out.println(arrayBlockingQueue.remove());
    }
    /**
     *有返回值,不抛出异常
     */
    public static void test2(){
    
    
        ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(arrayBlockingQueue.offer("a"));
        System.out.println(arrayBlockingQueue.offer("b"));
        System.out.println(arrayBlockingQueue.offer("c"));

        //System.out.println(arrayBlockingQueue.offer("d")); // false 不抛出异常!

        System.out.println("*****");
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll()); // null 不抛出异常!

    }
    /**
     *等待,阻塞(一直阻塞)
     */
    public static void test3() throws InterruptedException {
    
    
        ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        //一直阻塞
        arrayBlockingQueue.put("a");
        arrayBlockingQueue.put("b");
        arrayBlockingQueue.put("c");
        //arrayBlockingQueue.put("d"); //队列没有位置了,一直阻塞

        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take()); //没有数据了,一直阻塞

    }
 /**
     *等待,阻塞(等待超时)
     */
    public static void test4() throws InterruptedException {
    
    
        ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        arrayBlockingQueue.offer("a");
        arrayBlockingQueue.offer("b");
        arrayBlockingQueue.offer("c");
        arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS); //等待两秒,超时退出

        System.out.println("******");
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        arrayBlockingQueue.poll(2, TimeUnit.SECONDS); //等待两秒,超时退出
    }

SynchronousQueue synchronous queue

After entering an element, you must wait to take it out before you can put another element in it!

put()、take()

/**
 * 同步队列
 * SynchronousQueue 和 BlockingQueue 不一样,SynchronousQueue 不存储元素
 * put一个元素,必须从里面take取出来,否则不能再put进去值!(存一个,取一个,循环)
 */
public class SynchronousQueueDemo {
    
    
    public static void main(String[] args) {
    
    
        //存一个元素,取一个元素 循环
        SynchronousQueue<Object> synchronousQueue = new SynchronousQueue<>();

        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());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "->take " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "->take " + synchronousQueue.take());
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }, "T2").start();
    }
}

11. Thread pool (emphasis)

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

pooling technology

The program runs, the essence: occupying system resources! Optimize the use of resources! => pooling technology

Thread pool, connection pool, memory pool, object pool...

Creating and destroying is a waste of resources.

Pooling technology : Prepare some resources in advance. If someone wants to use them, they can come to me to get them, and return them to me after use, so as to improve efficiency.

Benefits of thread pool

1. Reduce resource consumption;

2. Improve the speed of response;

3. Convenient thread management.

Thread multiplexing, can control the maximum number of concurrency, manage threads

image-20210829182216583

Thread pool: three methods

/**
 * Executors 工具类:创建线程池  3大方法
 */
public class Demo1 {
    
    
    public static void main(String[] args) {
    
    
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单个线程的线程池
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建固定线程的线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();//创建可伸缩线程池

        try {
    
    
            for (int i = 0; i < 10; i++) {
    
    
                //使用了线程池之后,用线程池来创建线程
                threadPool.execute(() -> {
    
    
                    System.out.println(Thread.currentThread().getName() + " OK");
                });
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }

    }

}

Source code analysis: seven parameters

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>());
}
    
	//本质: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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

Manually create a thread pool

/**
 * Executors 工具类:创建线程池  3大方法
 *
 * 4大拒绝策略:
 * new ThreadPoolExecutor.AbortPolicy() //默认拒绝策略 银行满了,还有人进来,不处理这个人,抛出异常
 * new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里!
 * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常
 * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的竞争,也不会抛出异常!
 *
 */
public class Demo2 {
    
    
    public static void main(String[] args) {
    
    
        //工具类创建
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单个线程的线程池
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建固定线程的线程池
        //ExecutorService threadPool = Executors.newCachedThreadPool();//创建可伸缩线程池

        //手动创建线程池 ThreadPoolExecutor
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试和最早的竞争,也不会抛出异常!

        try {
    
    
            //最大承载:Queue + max
            //超出最大承载抛出RejectedExecutionException 异常 (默认拒绝策略)
            for (int i = 0; i < 9; i++) {
    
    
                //使用了线程池之后,用线程池来创建线程
                threadPool.execute(() -> {
    
    
                    System.out.println(Thread.currentThread().getName() + " OK");
                });
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }

    }

}

Four Rejection Strategies

image-20210830095348955

 * 4大拒绝策略:
 * new ThreadPoolExecutor.AbortPolicy() //默认拒绝策略 银行满了,还有人进来,不处理这个人,抛出异常
 * new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里!
 * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常
 * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的竞争,也不会抛出异常!

Summary and Extension

How to set the maximum number of threads in the thread pool?

Understand: IO-intensive, CPU-intensive (tuning)

public class Demo1 {
    
    
    public static void main(String[] args) {
    
    
        //手动创建线程池 ThreadPoolExecutor
        //最大线程到底如何定义?
        //1、CPU 密集型  电脑处理器数是几,就是几,可以保证CPU的效率最高!
        //2、IO 密集型   大于 程序中十分耗IO的线程数   ---> 程序中 15个大型任务 io十分占用资源! =》 30

        //获取CPU核数 电脑处理器数
        System.out.println(Runtime.getRuntime().availableProcessors());
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试和最早的竞争,也不会抛出异常!

        try {
    
    
            //最大承载:Queue + max
            //超出最大承载 RejectedExecutionException (默认拒绝策略)
            for (int i = 0; i < 9; i++) {
    
    
                //使用了线程池之后,用线程池来创建线程
                threadPool.execute(() -> {
    
    
                    System.out.println(Thread.currentThread().getName() + " OK");
                });
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }

    }

}

12. Four major functional interfaces (must be mastered)

image-20210830104308970

Programmers in the new era: lambda expression (essentially an instance of functional interface), chain programming, functional interface, Stream streaming computing

Functional interface: an interface with only one abstract method

//此注解用来判断该接口是否是函数式接口
@FunctionalInterface
public interface Runnable {
    
    
    public abstract void run();
}

//简化编程模型,在新版本的框架底层大量应用!
Function functional interface
image-20210830105803298

code testing

/**
 * Function 函数型接口 有一个输入,有一个输出
 * 只要是 函数式接口 可以用 lambda表达式简化
 */
public class Demo01 {
    
    
    public static void main(String[] args) {
    
    

        //输出输入的值
//        Function<String ,String > fun = new Function<String ,String >() {
    
    
//            @Override
//            public String apply(String str) {
    
    
//                return str;
//            }
//        };

        Function<String ,String > fun = (str) -> str; //lambda表达式

        System.out.println(fun.apply("123"));

    }
}
Predicate assertion interface
image-20210830111028318

code testing

/**
 * Predicate 断定型接口 有一个输入值 返回值是布尔值!
 */
public class Demo02 {
    
    
    public static void main(String[] args) {
    
    

        //判断字符串是否为空
//        Predicate<String> predicate = new Predicate<String>() {
    
    
//            @Override
//            public boolean test(String str) {
    
    
//                return "".equals(str);
//            }
//        };

        Predicate<String> predicate = str -> "".equals(str);

        System.out.println(predicate.test(""));
        System.out.println(predicate.test("123"));

    }
}
Consumer consumer interface
image-20210830112359042

code testing

/**
 * Consumer 消费型接口 只接收参数,不返回值
 */
public class Demo03 {
    
    
    public static void main(String[] args) {
    
    
        //接收参数,将其打印出来
//        Consumer<String> consumer = new Consumer<String>() {
    
    
//            @Override
//            public void accept(String str) {
    
    
//                System.out.println(str);
//            }
//        };

        Consumer<String> consumer = str -> System.out.println(str);

        consumer.accept("hello");

    }
}
Supplier supply interface
image-20210830112516994

code testing

/**
 * Supplier 供给型接口 不需参数,有返回值
 */
public class Demo04 {
    
    
    public static void main(String[] args) {
    
    
//        Supplier<String> supplier = new Supplier<String>() {
    
    
//            @Override
//            public String get() {
    
    
//                return "world";
//            }
//        };

        Supplier<String> supplier = () -> "world";

        System.out.println(supplier.get());
    }
}

13. Stream computing

What is Stream computing

Big Data: Storage + Compute

Storage: Collections, MySQL

Calculation: streaming computing~

image-20210830142933764

public class Test {
    
    
    public static void main(String[] args) {
    
    
        User user1 = new User(1, "a", 21);
        User user2 = new User(2, "b", 22);
        User user3 = new User(3, "c", 23);
        User user4 = new User(4, "d", 24);
        User user5 = new User(5, "e", 25);
        User user6 = new User(6, "f", 26);
        //存储交给集合
        List<User> list = Arrays.asList(user1, user2, user3, user4, user5, user6);

        //计算交给Stream流
        //lambda表达式、链式编程、函数式接口、Stream流式计算
        list.stream()
                .filter(user -> user.getId() % 2 == 0) //找出id为偶数的用户
                .filter(user -> user.getAge() > 23)  //年龄大于23岁
                .map(user -> user.getName().toUpperCase()) //用户名 转为大写
                .sorted((u1, u2) -> -u1.compareTo(u2)) //用户名字母到着排序
                .limit(1) //只输出一个用户
                .forEach(System.out::println);

    }
}

14、ForkJoin

What is ForkJoin

ForkJoin in JDK1.7, execute tasks in parallel! Improve efficiency ~. In the large amount of data rate will be faster!

In big data: the core idea of ​​MapReduce -> split big tasks into small tasks!

img

ForkJoin Features: Job Stealing!

The realization principle is: double-ended queue ! From above and below, you can go to get the task and execute it! (It maintains double-ended queues)

img

Use of Class ForkJoinPool

image-20210830155359296

ForkJoinTask

image-20210830155929025

Calculation class of ForkJoin:

/**
 * 求和计算的任务
 *
 * 如何使用 ForkJoin?
 * 1.ForkJoinPool 通过它来执行
 * 2.计算任务 forkJoinPool.execute(ForkJoinTask<?> task)
 * 3.计算类要继承ForkJoinTask
 */
public class ForkJoinDemo extends RecursiveTask<Long> {
    
    

    private Long start;
    private Long end;

    //临界值
    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 {
    
    
            //分支合并计算
            Long middle = (start + end) / 2;//中间值
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork(); //拆分任务,把线程任务压入线程队列
            ForkJoinDemo task2 = new ForkJoinDemo(middle, end);
            task2.fork(); //拆分任务,把线程任务压入线程队列

            //结果汇总
            return task1.join() + task2.join();
        }
    }
}

Test class:

/**
 * 三六九等 三 六(ForkJoin) 九(Stream并行流)
 */
public class Test {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //test1(); // 419
        //test2();
        test3();//234
    }

    //普通程序员
    public static void test1() {
    
    
        long sum = 0L;
        long start = System.currentTimeMillis();
        for (long i = 0L; i <= 10_0000_0000; i++) {
    
    
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum = " + sum + " 时间:" + (end - start));
    }

    //会使用forkJoin
    public static void test2() throws ExecutionException, InterruptedException {
    
    
        long start = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务
        Long sum = submit.get();

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

    public static void test3() {
    
    
        long start = System.currentTimeMillis();
        //Stream并行流计算 []
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);

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

.parallel().reduce(0, Long::sum) Use a parallel stream to calculate, improving efficiency. (Parallel Computing Reduction Sum)

15. Asynchronous callback

The original intention of Future design: to model the result of an event in the future!

image-20210830174618430

Thread asynchronous calls usually use the CompletableFuture class

(1) runAsync asynchronous callback with no return value

        //没有返回值的 runAsync 异步回调
        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("11111");

        completableFuture.get(); //阻塞,获取执行结果

(2) supplyAsync asynchronous callback with return value

/**
 * 类似异步调用:Ajax
 *
 * 异步调用:CompletableFuture
 * 成功回调
 * 失败回调
 */
public class Demo02 {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //有返回值的 supplyAsync 异步回调
        //成功和失败回调
        //返回的是错误信息
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println(Thread.currentThread().getName() + " supplyAsync => Integer");
            int i = 10 / 0;
            return 1024;
        });

       //成功回调
        System.out.println(completableFuture.whenComplete((t, u) -> {
    
    
            System.out.println("t=>" + t); //正常的返回结果
            System.out.println("u=>" + u); //错误信息 java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
        }).exceptionally((e -> {
    
    //失败回调
            System.out.println(e.getMessage());
            return 233; // 可以获取到错误的返回结果
        })).get());
    }
}

16、JMM

Please talk about your understanding of volatile

volatile is a lightweight synchronization mechanism provided by the Java virtual machine

1. Ensure visibility

2. Atomicity is not guaranteed

3. Prohibition of command rearrangement

What is JMM

JMM: Java memory model, things that don't exist, concepts! Promise!

Some synchronization conventions about JMM:

Threads are divided into working memory and main memory

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

2. Before the thread is locked, the latest value in the main memory must be read into the working memory;

3. Locking and unlocking are the same lock

8 operations

  • read (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, so that the subsequent load action can be used;
  • load (load) : Acts on the variable of the working memory, it puts the read operation from the variable in the main memory into the working memory;
  • use (use) : acts on the variables in the working memory, it transfers the variables in the working memory to the execution engine, whenever the virtual machine encounters a value that needs to use the variable, it will use this instruction;
  • Assign (assignment ): acts on variables in the working memory, it puts a value received from the execution engine into the variable copy of the working memory;
  • Store (storage) : acts on variables in the main memory, it transfers the value of a variable from the working memory to the main memory for subsequent write use;
  • write (write) : acts on the variable in the main memory, it puts the value of the variable obtained from the working memory by the store operation into the variable in the main memory;
  • lock (lock) : acts on the variables of the main memory, and marks a variable as a thread-exclusive state;
  • unlock (unlock) : A variable that acts on the main memory, it releases a variable that is in a locked state, and the released variable can be locked by other threads;

img

img

JMM has given corresponding regulations on these 8 operations:

  • One of read and load, store and write operations is not allowed to occur alone. That is, if you use read, you must load, and if you use store, you must write

  • The thread is not allowed to discard its latest assign operation, that is, after the data of the working variable has changed, the main memory must be notified

  • A thread is not allowed to synchronize data without assignment from working memory back to main memory

  • A new variable must be created in main memory, working memory is not allowed to directly use an uninitialized variable. That is, before implementing use and store operations on variables, they must go through assign and load operations

  • Only one thread can lock a variable at a time. After multiple locks, the same number of unlocks must be executed to unlock

  • If the lock operation is performed on a variable, the value of this variable in all working memory will be cleared. Before the execution engine uses this variable, the value of the initialized variable must be re-loaded or assigned.

  • If a variable is not locked, it cannot be unlocked. Nor can you unlock a variable that is locked by another thread

  • Before unlocking a variable, the variable must be synchronized back to main memory

Problem: Program A does not know that the value in main memory has changed

img

17、volatile

1) Guaranteed visibility

public class JMMDemo {

    // 如果不加volatile 程序会死循环
    // 加了volatile是可以保证可见性的,volatile保证一旦数据被修改,其它线程立马能够感知到
    private volatile static int num = 0;

    public static void main(String[] args) { // main 线程

        new Thread(()->{ // 线程1  不知道主内存中的值发生了变化
            while (num == 0){

            }
        }).start();

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

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

2) Atomicity is not guaranteed

Atomicity: indivisible

When thread A is performing tasks, it cannot be disturbed or divided. It either succeeds at the same time or fails at the same time.

/**
 * 不保证原子性
 */
public class VDemo2 {
    
    

    // volatile 不保证原子性
    private volatile static int num = 0;

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

    public static void main(String[] args) {
    
    

        // 20个线程,每个线程调用100次  理论值 2万
        for (int i = 0; i < 20; i++) {
    
    
            new Thread(() -> {
    
    
                for (int j = 0; j < 1000; j++) {
    
    
                    add();
                }
            }).start();
        }

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

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

How to ensure atomicity if lock and synchronized are not added?

img

Use atomic classes to solve atomicity problems

img

public class VDemo2 {
    
    

    // volatile 不保证原子性
    // 原子类的 Integer
    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add() {
    
    
        //num++; //不是原子性操作
        num.getAndIncrement(); // +1 操作 底层是CAS保证的原子性
    }

    public static void main(String[] args) {
    
    

        // 20个线程,每个线程调用100次  理论值 2万
        for (int i = 0; i < 20; i++) {
    
    
            new Thread(() -> {
    
    
                for (int j = 0; j < 1000; j++) {
    
    
                    add();
                }
            }).start();
        }

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

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

The bottom layer of the atomic class is directly linked to the operating system! Modify the value in memory!

Unsafe class is a very special existence

3) Prohibition of command rearrangement

command rearrangement

What is instruction rearrangement?

The programs we write, the computer does not execute as we write .

Source code –> compiler optimization rearrangement –> instruction parallelism may also be rearranged –> memory system will also be rearranged –> execution

When the processor performs instruction rearrangement, it will consider the dependencies between data!

int x=1; //1
int y=2; //2
x=x+5;   //3
y=x*x;   //4

//我们期望的执行顺序是 1234  可能执行的顺序会变成2134 1324
//可不可能是 4123? 不可能的

Possible impact results: Premise: The four values ​​of abxy are all 0 by default

thread A thread B
x=a y=b
b=1 a=2

Normal result: x = 0; y =0;

thread A thread B
b=1 a=2
x=a y=b

Possible weird results: x = 2; y = 1; (very low probability)

volatile can avoid instruction rearrangement:

A memory barrier will be added to volatile, and this memory barrier can guarantee the order of instructions in this barrier.

Memory barriers: CPU instructions. effect:

1. Guarantee the execution order of specific operations;

2. The memory visibility of certain variables can be guaranteed (using these features, the visibility of volatile implementations can be guaranteed)

img

Summarize:

volatile can guarantee visibility; atomicity cannot be guaranteed; due to memory barriers, it can be guaranteed to avoid instruction rearrangement!

Interviewer: So do you know where this memory barrier is used the most? singleton pattern

18. Completely play with the singleton mode

Hungry style, DCL lazy style

1) Hungry Chinese style

/**
 * 饿汉式单例
 * 核心思想:构造器私有化
 */
public class Hungry {
    
    

    // 可能浪费内存空间
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private byte[] data4 = new byte[1024*1024];

    private static final Hungry HUNGRY = new Hungry();

    private Hungry(){
    
    }

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

2) DCL lazy style

/**
 * 懒汉式单例
 * 道高一尺,魔高一丈!
 */
public class LazyMan {
    
    

    private volatile static LazyMan lazyMan;
    private static boolean flag = false;

    private LazyMan() {
    
    

        synchronized (LazyMan.class) {
    
    
            if (!flag) {
    
    
                flag = true;
            } else {
    
    
                throw new RuntimeException("不要试图使用反射破坏异常!");
            }

        }
    }

    //双重检测锁模式的 懒汉式单例 DCL懒汉式
    public static LazyMan getInstance() {
    
    
        if (null == lazyMan) {
    
    
            synchronized (LazyMan.class) {
    
    
                if (null == lazyMan) {
    
    
                    lazyMan = new LazyMan(); // 不是一个原子性操作
                }
            }
        }
        return lazyMan;
    }

    //不加 synchronized 多线程情况下,不一定是单例
    public static void main(String[] args) throws Exception {
    
    
//        for (int i = 0; i < 10; i++) {
    
    
//            new Thread(() -> {
    
    
//                LazyMan.getInstance();
//            }).start();
//        }

        //反射!
        //LazyMan instance = LazyMan.getInstance();

        Field flag = LazyMan.class.getDeclaredField("flag");
        flag.setAccessible(true);

        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        constructor.setAccessible(true);

        LazyMan instance = constructor.newInstance();

        flag.set(instance,false);

        LazyMan instance1 = constructor.newInstance();
        System.out.println(instance == instance1);
    }

          /*
            创建对象的步骤:
            1.分配内存空间
            2.执行构造方法,初始化对象
            3.把这个对象指向这个空间

             123
             132 线程A
                 线程B // 此时lazyMan还没有完成构造
             */
}

3) Static inner class

/**
 *  静态内部类
 */
public class Holder {
    
    

    private Holder(){
    
    

    }

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

    }

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

Singletons are not safe because of reflection

4) Enumeration class

/**
 * enum 是什么? 本身也是一个Class类
 */
public enum EnumSingle {
    
    

    INSTANCE;

    public EnumSingle getInstance(){
    
    
        return INSTANCE;
    }
}

class Test{
    
    
    public static void main(String[] args) throws Exception {
    
    
        EnumSingle instance1 = EnumSingle.INSTANCE;
        //EnumSingle instance2 = EnumSingle.INSTANCE;

        Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        EnumSingle instance2 = constructor.newInstance(); //java.lang.NoSuchMethodException: com.lkl.singleton.EnumSingle.

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

Using enumerations, we can prevent reflection from breaking

image-20210830223705420

The final decompiled source code of the enumeration type:

public final class EnumSingle extends Enum
{
    
    

    public static EnumSingle[] values()
    {
    
    
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
    
    
        return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
    
    
        super(s, i);
    }

    public EnumSingle getInstance()
    {
    
    
        return INSTANCE;
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {
    
    
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
    
    
            INSTANCE
        });
    }
}

19. In-depth understanding of CAS

What is CAS

Big factories must study the bottom layer! Breakthrough! Self-cultivation, weak foundation, ground-shaking operating system, computer network principles

public class CASDemo {
    
    

    //CAS  compareAndSet:比较并交换!
    public static void main(String[] args) {
    
    
        AtomicInteger atomicInteger = new AtomicInteger(2021);

        //期望、更新
        //public final boolean compareAndSet(int expect, int update)
        //如果我期望的值达到了,就更新,否则,就不更新,CAS 是CPU的并发原语!
        System.out.println(atomicInteger.compareAndSet(2021, 2022));
        System.out.println(atomicInteger.get());

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

Unsafe class

img

atomicInteger.getAndIncrement();
img

CAS: Compare the value currently in working memory with the value in main memory, and if the value is expected, perform the operation! If not, it keeps looping, using a spin lock.

shortcoming:

  • Loops take time;
  • The atomicity of only one shared variable can be guaranteed at a time;
  • it will have ABA problem

CAS: ABA (civet cat for prince)

img

A=1 in main memory

Thread 1: The expected value is 1, and it will become 2;

Thread 2: Two operations:

  1. Expected value is 1, becomes 3
  2. Expected to be 3, becomes 1

So for thread 1, the value of A is still 1, so there is a problem, and thread 1 is fooled; thread 1 does not know that the value of A has been modified!

public class CASDemo2 {
    
    

    //CAS  compareAndSet:比较并交换!
    public static void main(String[] args) {
    
    
        AtomicInteger atomicInteger = new AtomicInteger(2021);


        //期望、更新
        //public final boolean compareAndSet(int expect, int update)
        //如果我期望的值达到了,就更新,否则,就不更新,CAS 是CPU的并发原语!
        // ======================= 捣乱的线程 ==============================
        System.out.println(atomicInteger.compareAndSet(2021, 2022));
        System.out.println(atomicInteger.get());

        System.out.println(atomicInteger.compareAndSet(2022, 2021));
        System.out.println(atomicInteger.get());
        // ======================= 捣乱的线程 ==============================

        //======================== 期望的线程 ==============================
        System.out.println(atomicInteger.compareAndSet(2021, 6666));
        System.out.println(atomicInteger.get());
    }
}

20. Atomic Reference (AtomicReference)


Solve the ABA problem and introduce atomic references! Corresponding thought: Optimistic lock !

Atomic operations with version numbers!

public class CASDemo3 {
    
    

    //CAS  compareAndSet:比较并交换!
    public static void main(String[] args) {
    
    

        //AtomicStampedReference 泛型如果使用包装类,注意对象引用问题
        //正常在业务操作,这里泛型都是一个个对象
        AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(1, 1);

        new Thread(() -> {
    
    
            int stamp = stampedReference.getStamp(); //获得版本号
            System.out.println(Thread.currentThread().getName() + " 1 -> " + stamp);

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

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

            System.out.println(Thread.currentThread().getName() + " 2 -> " + stampedReference.getStamp());

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

            System.out.println(Thread.currentThread().getName() + " 3 -> " + stampedReference.getStamp());

        }, "a").start();

        new Thread(() -> {
    
    
            int stamp = stampedReference.getStamp(); //获得版本号
            System.out.println(Thread.currentThread().getName() + " 1 -> " + stamp);

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

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

            System.out.println(Thread.currentThread().getName() + " 2 -> " + stampedReference.getStamp());
        }, "b").start();

        try {
    
    
            TimeUnit.SECONDS.sleep(3);
            System.out.println("**************");
            System.out.println(stampedReference.getReference());
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

Notice:

Integer uses the object cache mechanism, the default range is -128~127, it is recommended to use the static factory method valueOf to obtain object instances instead of new, because valueOf uses cache, and new will definitely create new objects and allocate new memory space

integer wrapper
image-20210831110834295

21. Understanding of various locks

1. Fair lock and unfair lock

Fair lock: very fair, no queue jumping, first come, first served! (possibly less efficient)

Unfair lock: very unfair, you can jump in the queue (the default is unfair, and the efficiency is higher)

    public ReentrantLock() {
    
    
        sync = new NonfairSync();
    }

Constructor with parameters, which can modify the fair state

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

2. Reentrant lock

Reentrant lock (recursive lock): After getting the outer lock, it will automatically get the inner lock (synchronized [implicit] and Lock [explicit] are both reentrant locks)

image-20221005165339882

synchronized version

public class Demo01 {
    
    
    public static void main(String[] args) {
    
    
        Phone phone = new Phone();

        new Thread(()->{
    
    
            phone.sms();
        },"A").start();

        new Thread(()->{
    
    
            phone.sms();
        },"B").start();

    }
}

class Phone{
    
    
    public synchronized void sms(){
    
    
        System.out.println(Thread.currentThread().getName() + " sms");
        call();
    }

    public synchronized void call(){
    
    
        System.out.println(Thread.currentThread().getName() + " call");
    }
}

Lock version

public class Demo02 {
    
    
    public static void main(String[] args) {
    
    
        Phone2 phone = new Phone2();

        new Thread(()->{
    
    
            phone.sms();
        },"A").start();

        new Thread(()->{
    
    
            phone.sms();
        },"B").start();

    }
}

class Phone2{
    
    
    private final Lock lock = new ReentrantLock();

    public void sms(){
    
    
        lock.lock(); //Lock锁必须配对,有加锁就必须有解锁! 否则就会死锁!
        try {
    
    
            System.out.println(Thread.currentThread().getName() + " sms");
            call();
        } finally {
    
    
            lock.unlock();
        }
    }

    public void call(){
    
    
        lock.lock();
        try {
    
    
            System.out.println(Thread.currentThread().getName() + " call");
        } finally {
    
    
            lock.unlock();
        }
    }
}

3. Spin lock

spinlock

image-20210831143626881

Custom spin lock :

public class spinlock {
    
    

    // int 0
    // Thread null
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    //加锁
    public void myLock(){
    
    
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + " ==> myLock");

        //自旋锁
        while (!atomicReference.compareAndSet(null,thread)){
    
    

        }
    }

    //解锁
    public void myUnLock(){
    
    
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + " ==> myUnLock");

        atomicReference.compareAndSet(thread,null);
    }
}

Test a custom spinlock:

public class TestSpinLock {
    
    
    public static void main(String[] args) {
    
    
//        Lock lock = new ReentrantLock();
//        lock.lock();
//        lock.unlock();

        // 底层使用的自旋锁CAS
        spinlock spinlock = new spinlock();

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

        new Thread(() -> {
    
    
            spinlock.myLock();

            try {
    
    
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                spinlock.myUnLock();
            }

        }, "T2").start();

    }
}
output result
image-20210831150358948

Result analysis:

Two threads jointly operate a lock, and whoever gets the lock first executes first. The T1 thread first gets the lock and locks it, followed by the T2 thread, which first inputs the first line and then outputs the second line; the T1 thread releases the lock after 4s, and then the T2 thread gets the lock and locks it for operation, and releases the lock after 3s. Therefore: input the first and second lines first, output the third line after 4s, and output the fourth line after 3s.

4. Deadlock

What is deadlock? During the execution process, two or more processes wait for each other due to competition for resources. If there is no external interference, they cannot continue to execute.

Deadlock occurs when two threads try to acquire each other's lock while holding their own lock.

img

Deadlock test, how to eliminate deadlock!

/**
 * 死锁样例
 */
public class DeadLockDemo {
    
    

    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) {
    
    
        new Thread(() -> {
    
    
            synchronized (a) {
    
    
                System.out.println(Thread.currentThread().getName() + " 获取到锁a,试图获取锁b");
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                synchronized (b) {
    
    
                    System.out.println("获取到锁b");
                }
            }
        }, "A").start();

        new Thread(() -> {
    
    
            synchronized (b) {
    
    
                System.out.println(Thread.currentThread().getName() + " 获取到锁b,试图获取锁a");
                try {
    
    
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                synchronized (a) {
    
    
                    System.out.println("获取到锁a");
                }
            }
        }, "B").start();
    }
}

image-20221006000918655

Causes of deadlock:

First: Insufficient system resources

Second: The progress sequence of the process running is not appropriate

Third: Misallocation of resources

How to locate the deadlock and solve the problem?

1. Use jps to locate the process number, under the bin directory of jdk: there is a jps

Order:jps -l

image-20210831153959567

2. Use jstackthe process process number to find the deadlock information (jstack is a stack tracking tool that comes with jvm)

Order:jstack 进程号

image-20210831154245165

Interview, working! How to troubleshoot?

1. Log 90%

2. Stack information 10%

Guess you like

Origin blog.csdn.net/qq_43417581/article/details/127217919