Java学习笔记(No.21)

Java之JUC并发编程(No.21)

从JDK 1.5开始,新增一个处理线程的工具包“JUC”(即,java.util .concurrent工具包的简称)

1、进程与线程(Processes And Threads)

一个进程可以包含多个线程,但至少包含一个线程。其中Java默认有2个线程(main线程与gc线程)

Java不能直接开启线程,如以下源码所示

//使用Thread类start方法开启线程时,其实是调用底层C++编写的本地方法来开启线程(即,Java无法直接操作硬件)
    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 */
            }
        }
    }
    private native void start0();

2、并发与并行(Concurrency And Parallelism)

并发编程的本质是“充分利用CPU的资源”

  • 2.1、并发(Concurrency)

    • 并发是指两个或多个事件在同一时间间隔内发生(即,CPU单核,多线程操作同一个资源)
  • 2.2、并行(Parallelism)

    • 并行是指两个或者多个事件在同一时刻发生(即,CPU多核,多线程操作不同资源)
  • 2.3、查看CPU核数(View CPU Cores Numbers)

    其示例代码,如下所示

package com.xueshanxuehai.juc;
/**
 * 查看CPU核数
 */
public class ViewCPUCoresNumbers {
    public static void main(String[] args) {
        //获取CPU核数:CPU密集型,IO密集型
        System.out.println("当前系统CPU内核数量:" + Runtime.getRuntime().availableProcessors());
    }
}

其运行结果,如下所示

当前系统CPU内核数量:8

3、线程状态(Thread State)

在JDK中,线程共有6种状态,如下所示

  • 3.1、新生状态(“NEW”)
  • 3.2、运行状态(“RUNNABLE”)
  • 3.3、阻塞状态(“BLOCKED”)
  • 3.4、一直等待状态(“WAITING”)
  • 3.5、超时等待状态(“TIMED_WAITING”)
  • 3.6、终止状态(“TERMINATED”)

4、wait方法与sleep方法的区别(The Difference Between Wait Method And Sleep Method)

使用wait方法与sleep方法时,都需要抛出捕获异常“InterruptException(即,中断异常)”

  • 4.1、所属类不同
    • 4.1.1、wait方法来自Object类
    • 4.1.2、sleep方法来自Thread类
  • 4.2、释放锁不同
    • 4.2.1、wait方法会释放锁
    • 4.2.2、sleep方法不会释放锁
  • 4.3、使用范围不同
    • 4.3.1、wait方法必须在同步代码块中使用
    • 4.3.2、sleep方法可以在任何地方使用

5、Synchronized锁(Synchronized Lock)

  • 5.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.synchronizedlock;
    /**
     * 销售票:使用Synchronized锁
     * 一般公司真正的多线程开发要求:降低耦合性
     * 其中线程就是一个单独的资源类(只有属性与方法),没有任何附属的操作
     */
    public class SaleTicket {
        public static void main(String[] args) {
            //并发:多线程操作同一个资源类,把资源类丢入线程
            Ticket ticket = new Ticket();
            //@FunctionInterface 函数式接口:jdk1.8,Lambda表达式:其语法格式为“(参数)->{代码体}”
            new Thread(() -> {for (int i = 0; i < 10; i++) ticket.saleTicket();},"1").start();
            new Thread(() -> {for (int i = 0; i < 10; i++) ticket.saleTicket();},"2").start();
        }
    }
    //资源类(OOP)
    class Ticket {
        //属性
        private int number = 20;//总票数
        private int count = 0;//累计卖票数
        /**
         * 方法:卖票
         * synchronized的本质:队列、锁
         */
        public synchronized void saleTicket() {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "线程卖出第" + (++count) + "张票,还剩" + (--number)+ "张票");
            }
        }
    }
    
  • 5.2、运行结果(Run Result)

    其运行结果,如下所示

    1线程卖出第1张票,还剩19张票
    1线程卖出第2张票,还剩18张票
    1线程卖出第3张票,还剩17张票
    1线程卖出第4张票,还剩16张票
    1线程卖出第5张票,还剩15张票
    1线程卖出第6张票,还剩14张票
    1线程卖出第7张票,还剩13张票
    1线程卖出第8张票,还剩12张票
    1线程卖出第9张票,还剩11张票
    1线程卖出第10张票,还剩10张票
    2线程卖出第11张票,还剩9张票
    2线程卖出第12张票,还剩8张票
    2线程卖出第13张票,还剩7张票
    2线程卖出第14张票,还剩6张票
    2线程卖出第15张票,还剩5张票
    2线程卖出第16张票,还剩4张票
    2线程卖出第17张票,还剩3张票
    2线程卖出第18张票,还剩2张票
    2线程卖出第19张票,还剩1张票
    2线程卖出第20张票,还剩0张票
    

6、Lock锁(Lock Lock)

  • 6.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.locklock;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    /**
     * 销售票:使用Lock锁
     * 一般公司真正的多线程开发要求:降低耦合性
     * 其中线程就是一个单独的资源类(只有属性与方法),没有任何附属的操作
     */
    public class SaleTicket {
        public static void main(String[] args) {
            //并发:多线程操作同一个资源类,把资源类丢入线程
            Ticket ticket = new Ticket();
            //@FunctionInterface 函数式接口:jdk1.8,Lambda表达式:其语法格式为“(参数)->{代码体}”
            new Thread(() -> {for (int i = 0; i < 10; i++) ticket.saleTicket();},"1").start();
            new Thread(() -> {for (int i = 0; i < 10; i++) ticket.saleTicket();},"2").start();
        }
    }
    /**
     * 资源类(OOP):使用Lock锁
     * 步骤1:Lock lock=new ReentrantLock();//创建锁
     * 步骤2:lock.lock();//加锁
     * 步骤3:lock.unlock();//解锁
     */
    class Ticket {
        //属性
        private int number = 20;//总票数
        private int count = 0;//累计卖票数
        Lock lock=new ReentrantLock();//创建锁
        /**
         * 方法:卖票
         * synchronized的本质:队列、锁
         */
        public synchronized void saleTicket() {
            lock.lock();//加锁
            try {
                //业务代码
                if (number > 0) {
                    System.out.println(Thread.currentThread().getName() + "线程卖出第" + (++count) + "张票,还剩" + (--number)+ "张票");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();//解锁
            }
        }
    }
    
  • 6.2、运行结果(Run Result)

    其运行结果,如下所示

    1线程卖出第1张票,还剩19张票
    1线程卖出第2张票,还剩18张票
    1线程卖出第3张票,还剩17张票
    1线程卖出第4张票,还剩16张票
    1线程卖出第5张票,还剩15张票
    1线程卖出第6张票,还剩14张票
    1线程卖出第7张票,还剩13张票
    1线程卖出第8张票,还剩12张票
    1线程卖出第9张票,还剩11张票
    1线程卖出第10张票,还剩10张票
    2线程卖出第11张票,还剩9张票
    2线程卖出第12张票,还剩8张票
    2线程卖出第13张票,还剩7张票
    2线程卖出第14张票,还剩6张票
    2线程卖出第15张票,还剩5张票
    2线程卖出第16张票,还剩4张票
    2线程卖出第17张票,还剩3张票
    2线程卖出第18张票,还剩2张票
    2线程卖出第19张票,还剩1张票
    2线程卖出第20张票,还剩0张票
    

7、synchronized锁与Lock锁的区别(The Difference Synchronized Lock And Lock Lock)

  • 7.1、"synchronized"是“内置的Java关键字”,"Lock"是“Java接口”
  • 7.2、"synchronized"无法获取锁的状态,"Lock"可以判断是否获取到了锁
  • 7.3、"synchronized"会自动释放锁,"Lock"必须要手动释放锁,若不释放锁则会死锁
  • 7.4、"synchronized"中一个线程阻塞时另一个线程会一直等待,"Lock"中一个线程阻塞时另一个线程不一定会一直等待
  • 7.5、"synchronized"可重入锁,不可以中断,非公平锁(十分不公平,可以插队),"Lock"可重入锁,可以判断锁,可手动设置非公平锁(十分不公平,可以插队)或公平锁(十分公平,必须排队),默认为非公平锁
  • 7.6、"synchronized"适合锁定少量的代码同步问题,"Lock"适合锁定大量的代码同步问题

8、生产者与消费者问题(Producer And Consumer Problem)

8.1、使用Synchronized锁

  • 8.1.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.synchronizedlock;
    /**
     * 线程通信之间的生产者与消费者问题:使用Synchronized锁
     */
    public class ProducerAndConsumerProblem {
        public static void main(String[] args) {
            System.out.println("线程通信之间的生产者与消费者问题(使用Synchronized锁)");
            Resource resource = new Resource();
            new Thread(()->{for (int i = 0; i < 10; i++) resource.increment();}, "1").start();
            new Thread(()->{for (int i = 0; i < 10; i++) resource.decrement();}, "2").start();
            new Thread(()->{for (int i = 0; i < 10; i++) resource.increment();}, "3").start();
            new Thread(()->{for (int i = 0; i < 10; i++) resource.decrement();}, "4").start();
        }
    }
    /**
     * 资源类(OOP):使用Synchronized锁
     */
    class Resource {
        private int number = 0;//资源数量
        public synchronized void increment() {//生产资源方法
            while (number != 0) {//切勿使用if语句,否则会产生“虚假唤醒”问题
                try {
                    this.wait();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "线程生产资源数量" + (++number));//业务代码
            this.notifyAll();//通知唤醒其他所有线程
        }
        public synchronized void decrement() {//消费资源方法
            while (number == 0) {//切勿使用if语句,否则会产生“虚假唤醒”问题
                try {
                    this.wait();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "线程生产资源数量" + (--number));//业务代码
            this.notifyAll();//通知唤醒其他所有线程
        }
    }
    
  • 8.1.2、运行结果(Run Result)

    其运行结果,如下所示

    线程通信之间的生产者与消费者问题(使用Synchronized)
    1线程生产资源数量1
    4线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    

8.2、使用Lock锁

  • 8.2.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.locklock;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    /**
     * 线程通信之间的生产者与消费者问题:使用Lock锁
     */
    public class ProducerAndConsumerProblem {
        public static void main(String[] args) {
            System.out.println("线程通信之间的生产者与消费者问题(使用Lock锁)");
            Resource resource = new Resource();
            new Thread(()->{for (int i = 0; i < 10; i++) resource.increment();}, "1").start();
            new Thread(()->{for (int i = 0; i < 10; i++) resource.decrement();}, "2").start();
            new Thread(()->{for (int i = 0; i < 10; i++) resource.increment();}, "3").start();
            new Thread(()->{for (int i = 0; i < 10; i++) resource.decrement();}, "4").start();
        }
    }
    /**
     * 资源类(OOP):使用Lock锁
     */
    class Resource {
        private int number = 0;//资源数量
        Lock lock=new ReentrantLock();//创建Lock锁对象
        Condition condition=lock.newCondition();//创建Condition接口实例
        public void increment() {//生产资源方法
            lock.lock();//加锁
            try {
                //业务代码
                while (number != 0) {//切勿使用if语句,否则会产生“虚假唤醒”问题
                    condition.await();//等待
                }
                System.out.println(Thread.currentThread().getName() + "线程生产资源数量" + (++number));
                condition.signalAll();//通知唤醒其他所有线程
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();//解锁
            }
        }
        public void decrement() {//消费资源方法
            lock.lock();//加锁
            try {
                //业务代码
                while (number == 0) {//切勿使用if语句,否则会产生“虚假唤醒”问题
                    condition.await();//等待
                }
                System.out.println(Thread.currentThread().getName() + "线程生产资源数量" + (--number));
                condition.signalAll();//通知唤醒其他所有线程
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();//解锁
            }
        }
    }
    
  • 8.2.2、运行结果(Run Result)

    其运行结果,如下所示

    线程通信之间的生产者与消费者问题(使用Lock)
    1线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    2线程生产资源数量0
    3线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    1线程生产资源数量1
    4线程生产资源数量0
    

8.3、注意事项(Matters Needing Attention)

  • 8.3.1、判断线程等待时不能用if判断(会产生虚假唤醒问题),而要用while判断(不会产生虚假唤醒问题)

9、Condition接口实例实现线程精准通知唤醒(Condition Interface Instance Realizes Thread Accurate Notification Wake-Up)

  • 9.1、示例代码(Smaple Code)

    其示例代码,如下所示

package com.xueshanxuehai.juc.locklock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 线程精准通知唤醒:使用Lock锁的Condition接口实例实现“1、先打开线程1,2、再打开线程2,3、再打开线程3,4、然后依此循环执行”
 */
public class ThreadAccurateNotificationWakeUp {
    public static void main(String[] args) {
        System.out.println("使用Lock锁的Condition接口实例实现线程精准通知唤醒");
        Resource1 resource1 = new Resource1();
        new Thread(()->{for (int i = 0; i < 10; i++) resource1.wakeUp1();}, "1").start();
        new Thread(()->{for (int i = 0; i < 10; i++) resource1.wakeUp2();}, "2").start();
        new Thread(()->{for (int i = 0; i < 10; i++) resource1.wakeUp3();}, "3").start();
    }
}
/**
 * 资源类(OOP):使用Lock锁
 */
class Resource1 {
    private int number = 1;//线程编号(默认指定线程1)
    Lock lock=new ReentrantLock();//创建Lock锁对象
    Condition condition1=lock.newCondition();//创建Condition接口实例1
    Condition condition2=lock.newCondition();//创建Condition接口实例2
    Condition condition3=lock.newCondition();//创建Condition接口实例3
    public void wakeUp1() {//生产资源方法
        lock.lock();//加锁
        try {
            //业务代码
            while (number != 1) {//切勿使用if语句,否则会产生“虚假唤醒”问题
                condition1.await();//等待
            }
            System.out.println(Thread.currentThread().getName() + "线程被唤醒");
            number=2;//指定线程2
            condition2.signal();//通知唤醒指定线程2
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
    public void wakeUp2() {//生产资源方法
        lock.lock();//加锁
        try {
            //业务代码
            while (number != 2) {//切勿使用if语句,否则会产生“虚假唤醒”问题
                condition2.await();//等待
            }
            System.out.println(Thread.currentThread().getName() + "线程被唤醒");
            number=3;//指定线程3
            condition3.signal();//通知唤醒指定线程3
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
    public void wakeUp3() {//生产资源方法
        lock.lock();//加锁
        try {
            //业务代码
            while (number != 3) {//切勿使用if语句,否则会产生“虚假唤醒”问题
                condition3.await();//等待
            }
            System.out.println(Thread.currentThread().getName() + "线程被唤醒");
            number=1;//指定线程1
            condition1.signal();//通知唤醒指定线程1
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
}
  • 9.2、运行结果(Run Result)

    其运行结果,如下所示

使用Lock锁的Condition接口实例实现线程精准通知唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒
1线程被唤醒
2线程被唤醒
3线程被唤醒

10、八锁现象(Eight Lock Phenomenon)

  • 10.1、现象一(Phenomenon One)

    现象一:同一个对象,代码中先执行Synchronized锁线程1(发短信),再执行Synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”

    • 10.1.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.synchronizedlock.eightlockphenomenon;
      /**
       * 八锁现象一:同一个对象,代码中先执行Synchronized锁线程1(发短信),再执行Synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”
       */
      public class PhenomenonOne {
          public static void main(String[] args) {
              MobilePhone1 mobilePhone1 = new MobilePhone1();
              new Thread(()->{mobilePhone1.sendMsg();},"1").start();
              new Thread(()->{mobilePhone1.callPhone();},"2").start();
          }
      }
      //手机类
      class MobilePhone1{
          /**
           * synchronized锁的对象是方法的调用者(即,this)
           * 以下2个方法使用的是同一个 锁,所以谁先拿到谁执行
           */
          public synchronized void sendMsg(){
              System.out.println("发短信");
          }
          public synchronized void callPhone(){
              System.out.println("打电话");
          }
      }
      
    • 10.1.2、运行结果(Run Result)

      其运行结果,如下所示

      发短信
      打电话
      
  • 10.2、现象二(Phenomenon Two)

    现象二:同一个对象,代码中先执行Synchronized锁线程1(且先延时3秒再发短信),再执行Synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”

    • 10.2.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.synchronizedlock.eightlockphenomenon;
      import java.util.concurrent.TimeUnit;
      /**
       * 八锁现象二:同一个对象,代码中先执行Synchronized锁线程1(且先延时3秒再发短信),再执行Synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”
       */
      public class PhenomenonTwo {
          public static void main(String[] args) {
              MobilePhone2 mobilePhone2 = new MobilePhone2();
              new Thread(()->{mobilePhone2.sendMsg();},"1").start();
              new Thread(()->{mobilePhone2.callPhone();},"2").start();
          }
      }
      //手机类
      class MobilePhone2{
          /**
           * synchronized锁的对象是方法的调用者(即,this)
           * 以下2个方法使用的是同一个 锁,所以谁先拿到谁执行
           */
          public synchronized void sendMsg(){
      //        //延时(方式1)
      //        try {
      //            Thread.sleep(3000);//延时3秒
      //        } catch (InterruptedException e) {
      //            e.printStackTrace();
      //        }
              //延时(方式2)
              try {
                  TimeUnit.SECONDS.sleep(3);//延时3秒
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("发短信");
          }
          public synchronized void callPhone(){
              System.out.println("打电话");
          }
      }
      
    • 10.2.2、运行结果(Run Result)

      其运行结果,如下所示

      发短信
      打电话
      
  • 10.3、现象三(Phenomenon Three)

    现象三:同一个对象,代码中先执行Synchronized锁线程1(且先延时3秒再发短信),再执行普通线程2(打电话),此时的运行结果为“先打电话再发短信”

    • 10.3.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.synchronizedlock.eightlockphenomenon;
      import java.util.concurrent.TimeUnit;
      /**
       * 八锁现象三:同一个对象,代码中先执行Synchronized锁线程1(且先延时3秒再发短信),再执行普通线程2(打电话),此时的运行结果为“先打电话再发短信”
       */
      public class PhenomenonThree {
          public static void main(String[] args) {
              MobilePhone3 mobilePhone3 = new MobilePhone3();
              new Thread(() -> {mobilePhone3.sendMsg();}, "1").start();
              new Thread(() -> {mobilePhone3.callPhone();}, "2").start();
          }
      }
      //手机类
      class MobilePhone3 {
          /**
           * synchronized锁的对象是方法的调用者(即,this)
           */
          public synchronized void sendMsg() {
              try {
                  TimeUnit.SECONDS.sleep(3);//延时3秒
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("发短信");
          }
          /**
           * 此方法没有同步锁,不受锁的影响
           */
          public void callPhone() {
              System.out.println("打电话");
          }
      }
      
    • 10.3.2、运行结果(Run Result)

      其运行结果,如下所示

      打电话
      发短信
      
  • 10.4、现象四(Phenomenon Four)

    现象四:不同对象,代码中先执行对象1的Synchronized锁线程1(发短信),接着延时3秒,再执行对象2的Synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”

    • 10.4.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.synchronizedlock.eightlockphenomenon;
      import java.util.concurrent.TimeUnit;
      /**
       * 八锁现象四:不同对象,代码中先执行对象1的Synchronized锁线程1(发短信),接着延时3秒,再执行对象2的Synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”
       */
      public class PhenomenonFour {
          public static void main(String[] args) {
              MobilePhone4 mobilePhone41 = new MobilePhone4();
              MobilePhone4 mobilePhone42 = new MobilePhone4();
              new Thread(() -> {mobilePhone41.sendMsg();}, "1").start();
              try {
                  TimeUnit.SECONDS.sleep(3);//延时3秒
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              new Thread(() -> {mobilePhone42.callPhone();}, "2").start();
          }
      }
      //手机类
      class MobilePhone4 {
          /**
           * synchronized锁的对象是方法的调用者(即,this)
           */
          public synchronized void sendMsg() {
              System.out.println("发短信");
          }
          public synchronized void callPhone() {
              System.out.println("打电话");
          }
      }
      
    • 10.4.2、运行结果(Run Result)

      其运行结果,如下所示

      发短信
      打电话
      
  • 10.5、现象五(Phenomenon Five)

    现象五:同一对象,代码中先执行static synchronized锁线程1(且先延时3秒再发短信),再执行static synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”

    • 10.5.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.synchronizedlock.eightlockphenomenon;
      import java.util.concurrent.TimeUnit;
      /**
       * 八锁现象五:同一对象,代码中先执行static synchronized锁线程1(且先延时3秒再发短信),再执行static synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”
       */
      public class PhenomenonFive {
          public static void main(String[] args) {
              MobilePhone5 mobilePhone5 = new MobilePhone5();
              new Thread(() -> {mobilePhone5.sendMsg();}, "1").start();
              new Thread(() -> {mobilePhone5.callPhone();}, "2").start();
          }
      }
      //手机类
      class MobilePhone5 {
          /**
           * synchronized锁的对象是方法的调用者(即,this)
           * static synchronized是静态方法(且只会在类加载时生成),锁的是类(即,class)
           */
          public static synchronized void sendMsg() {
              try {
                  TimeUnit.SECONDS.sleep(3);//延时3秒
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("发短信");
          }
          public static synchronized void callPhone() {
              System.out.println("打电话");
          }
      }
      
    • 10.5.2、运行结果(Run Result)

      其运行结果,如下所示

      发短信
      打电话
      
  • 10.6、现象六(Phenomenon Six)

    现象六:不同对象,代码中先执行对象1的static synchronized锁线程1(发短信),接着延时3秒,再执行对象2的static synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”

    • 10.6.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.synchronizedlock.eightlockphenomenon;
      import java.util.concurrent.TimeUnit;
      /**
       * 八锁现象六:不同对象,代码中先执行对象1的static synchronized锁线程1(发短信),接着延时3秒,再执行对象2的static synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”
       */
      public class PhenomenonSix {
          public static void main(String[] args) {
              MobilePhone6 mobilePhone61 = new MobilePhone6();
              MobilePhone6 mobilePhone62 = new MobilePhone6();
              new Thread(() -> {mobilePhone61.sendMsg();}, "1").start();
              try {
                  TimeUnit.SECONDS.sleep(3);//延时3秒
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              new Thread(() -> {mobilePhone62.callPhone();}, "2").start();
          }
      }
      //手机类
      class MobilePhone6 {
          /**
           * synchronized锁的对象是方法的调用者(即,this)
           * static synchronized是静态方法(且只会在类加载时生成),锁的是类(即,class)
           */
          public static synchronized void sendMsg() {
              System.out.println("发短信");
          }
          public static synchronized void callPhone() {
              System.out.println("打电话");
          }
      }
      
    • 10.6.2、运行结果(Run Result)

      其运行结果,如下所示

      发短信
      打电话
      
  • 10.7、现象七(Phenomenon Seven)

    现象七:同一对象,代码中先执行static synchronized锁线程1(且先延时3秒再发短信),再执行synchronized锁线程2(打电话),此时的运行结果为“先打电话再发短信”

    • 10.7.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.synchronizedlock.eightlockphenomenon;
      import java.util.concurrent.TimeUnit;
      /**
       * 八锁现象七:同一对象,代码中先执行static synchronized锁线程1(且先延时3秒再发短信),再执行synchronized锁线程2(打电话),此时的运行结果为“先打电话再发短信”
       */
      public class PhenomenonSeven {
          public static void main(String[] args) {
              MobilePhone7 mobilePhone7 = new MobilePhone7();
              new Thread(() -> {mobilePhone7.sendMsg();}, "1").start();
              new Thread(() -> {mobilePhone7.callPhone();}, "2").start();
          }
      }
      //手机类
      class MobilePhone7 {
          /**
           * static synchronized是静态方法(且只会在类加载时生成),锁的是类(即,class)
           */
          public static synchronized void sendMsg() {
              try {
                  TimeUnit.SECONDS.sleep(3);//延时3秒
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println("发短信");
          }
          /**
           * synchronized锁的对象是方法的调用者(即,this)
           */
          public synchronized void callPhone() {
              System.out.println("打电话");
          }
      }
      
    • 10.7.2、运行结果(Run Result)

      其运行结果,如下所示

      打电话
      发短信
      
  • 10.8、现象八(Phenomenon Eight)

    现象八:不同对象,代码中先执行对象1的static synchronized锁线程1(发短信),接着延时3秒,再执行对象2的synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”

    • 10.8.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.synchronizedlock.eightlockphenomenon;
      import java.util.concurrent.TimeUnit;
      /**
       * 八锁现象八:不同对象,代码中先执行对象1的static synchronized锁线程1(发短信),接着延时3秒,再执行对象2的synchronized锁线程2(打电话),此时的运行结果为“先发短信再打电话”
       */
      public class PhenomenonEight {
          public static void main(String[] args) {
              MobilePhone8 mobilePhone81 = new MobilePhone8();
              MobilePhone8 mobilePhone82 = new MobilePhone8();
              new Thread(() -> {mobilePhone81.sendMsg();}, "1").start();
              try {
                  TimeUnit.SECONDS.sleep(3);//延时3秒
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              new Thread(() -> {mobilePhone82.callPhone();}, "2").start();
          }
      }
      //手机类
      class MobilePhone8 {
          /**
           * static synchronized是静态方法(且只会在类加载时生成),锁的是类(即,class)
           */
          public static synchronized void sendMsg() {
              System.out.println("发短信");
          }
          /**
           * synchronized锁的对象是方法的调用者(即,this)
           */
          public synchronized void callPhone() {
              System.out.println("打电话");
          }
      }
      
    • 10.8.2、运行结果(Run Result)

      其运行结果,如下所示

      发短信
      打电话
      
  • 10.9、注意事项(Matters Needing Attention)

    • 10.9.1、"synchronized同步方法"实际上锁定的是“对象(this)”
    • 10.9.2、"static synchronized同步方法"实际上锁定的是“类(class)”
    • 10.9.3、"普通方法"没有锁,不收锁的影响
    • 10.9.4、对象(this)为具体的事物(如:“某个手机”),类(class)为抽象的事物(如:“手机模板”)

11、不安全的集合类解决方案(Unsafe Collection Class Solution)

在JUC并发情形下,集合类是不安全的,会抛出“并发修改异常(ConcurrentModificationException)”

  • 11.1、CopyOnWriteArrayList

    • 11.1.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.UnsafeCollectionClass;
      import java.util.*;
      import java.util.concurrent.CopyOnWriteArrayList;
      /**
       * JUC并发编程时,使用ArrayList创建List集合会产生"并发修改异常"(ConcurrentModificationException)
       * 解决方案:推荐使用CopyOnWriteArrayList(写入时复制[一种计算机程序设计的优化策略],避免写入时覆盖数据问题)
       */
      public class CopyOnWriteArrayListClass {
          public static void main(String[] args) {
              /**
               * List<String> list = new ArrayList<>();
               * JUC并发下的ArrayList不安全:无线程同步安全管控机制
               * 解决方案:
               * 1、List<String> list = new Vector<>();//使用synchronized锁(JDK8或JDK16)
               * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());//使用synchronized互斥锁(JDK8或JDK16)
               * 3、List<String> list = new CopyOnWriteArrayList<>();//使用Lock锁(JDK8)或使用synchronized互斥锁(JDK16)
               */
              List<String> list = new CopyOnWriteArrayList<>();//推荐使用
              System.out.println("JUC并发编程时,使用CopyOnWriteArrayList创建List集合");
              for (int i = 0; i < 30; i++) {
                  new Thread(() -> {
                      list.add(UUID.randomUUID().toString().substring(0, 6));
                      System.out.println(list);
                  }, String.valueOf(i + 1)).start();
              }
              Set<String> set=new HashSet<>();
          }
      }
      
    • 11.1.2、运行结果(Run Result)

      其运行结果,如下所示

      JUC并发编程时,使用CopyOnWriteArrayList创建List集合
      [0b8b4d]
      [0b8b4d, e84376]
      [0b8b4d, e84376, c5ad34]
      [0b8b4d, e84376, c5ad34, 299ca7]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676, b3a85b]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676, b3a85b, 3e4436]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676, b3a85b, 3e4436, 8661a1]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676, b3a85b, 3e4436, 8661a1, 144ea0]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676, b3a85b, 3e4436, 8661a1, 144ea0, ab575b]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676, b3a85b, 3e4436, 8661a1, 144ea0, ab575b, e083f7]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676, b3a85b, 3e4436, 8661a1, 144ea0, ab575b, e083f7, e1d3a3]
      [0b8b4d, e84376, c5ad34, 299ca7, 38a335, fea1b4, 4f147d, 8556ff, 13180a, 34cd6e, 88944c, 641877, a9047e, 9334f2, f77f75, 8a91cb, a5473d, f4595d, 087f01, e7f9e9, 3b45b6, 213676, b3a85b, 3e4436, 8661a1, 144ea0, ab575b, e083f7, e1d3a3, 9732d0]
      
  • 11.2、CopyOnWriteArraySet

    • 11.2.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.UnsafeCollectionClass;
      import java.util.*;
      /**
       * JUC并发编程时,使用HashSet创建Set集合会产生"并发修改异常"(ConcurrentModificationException)
       * 解决方案:推荐使用CopyOnWriteArraySet(写入时复制[一种计算机程序设计的优化策略],避免写入时覆盖数据问题)
       */
      public class CopyOnWriteArraySetClass {
          public static void main(String[] args) {
              /**
               * Set<String> set = new HashSet<>();
               * JUC并发下的HashSet不安全:无线程同步安全管控机制
               * 解决方案:
               * 1、Set<String> set = Collections.synchronizedSet(new HashSet<>());//使用synchronized互斥锁(JDK8或JDK16)
               * 2、Set<String> set =new java.util.concurrent.CopyOnWriteArraySet<>();//使用Lock锁(JDK8)或使用synchronized互斥锁(JDK16)
               */
              Set<String> set =new java.util.concurrent.CopyOnWriteArraySet<>();//推荐使用
              System.out.println("JUC并发编程时,使用CopyOnWriteArraySet创建Set集合");
              for (int i = 0; i < 30; i++) {
                  new Thread(() -> {
                      set.add(UUID.randomUUID().toString().substring(0, 6));
                      System.out.println(set);
                  }, String.valueOf(i + 1)).start();
              }
          }
      }
      
    • 11.2.2、运行结果(Run Result)

      其运行结果,如下所示

      JUC并发编程时,使用CopyOnWriteArraySet创建Set集合
      [bf59da]
      [bf59da, de2c83]
      [bf59da, de2c83, e14e70]
      [bf59da, de2c83, e14e70, 308388]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf, 15e497]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf, 15e497, aaf82e]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf, 15e497, aaf82e, 540f20]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf, 15e497, aaf82e, 540f20, 5bfef7]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf, 15e497, aaf82e, 540f20, 5bfef7, 88b280]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf, 15e497, aaf82e, 540f20, 5bfef7, 88b280, ddb27c]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf, 15e497, aaf82e, 540f20, 5bfef7, 88b280, ddb27c, 1793b7]
      [bf59da, de2c83, e14e70, 308388, 8e8fcf, a8fed8, 28e255, e2a17a, dc59cb, ff1670, 8bec08, ec5d85, 93a589, a37b8d, e3ae54, d22783, 775e33, 46bed5, 585e57, a17cde, eb69a5, b9b4bf, 15e497, aaf82e, 540f20, 5bfef7, 88b280, ddb27c, 1793b7, aafcba]
      
  • 11.3、ConcurrentHashMap

    • 11.3.1、示例代码(Smaple Code)

      其示例代码,如下所示

      package com.xueshanxuehai.juc.UnsafeCollectionClass;
      import java.util.Map;
      import java.util.UUID;
      import java.util.concurrent.ConcurrentHashMap;
      /**
       * JUC并发编程时,使用HashMap创建Map集合会产生"并发修改异常"(ConcurrentModificationException)
       * 解决方案:推荐使用ConcurrentHashMap(写入时复制[一种计算机程序设计的优化策略],避免写入时覆盖数据问题)
       */
      public class ConcurrentHashMapClass {
          public static void main(String[] args) {
              /**
               * Map<String, String> map = new HashMap<>();//默认等价于Map<String, String> map = new HashMap<>(16,0.75f);
               * JUC并发下的HashMap不安全:无线程同步安全管控机制
               * 解决方案:
               * 1、Map<String, String> map = Collections.synchronizedMap(new HashMap<>());//使用synchronized互斥锁(JDK8或JDK16)
               * 2、Map<String, String> map =new ConcurrentHashMap<>();//使用synchronized互斥锁(JDK8或JDK16)
               */
              Map<String, String> map =new ConcurrentHashMap<>();//推荐使用
              System.out.println("JUC并发编程时,使用ConcurrentHashMap创建Map集合");
              for (int i = 0; i < 30; i++) {
                  new Thread(() -> {
                      map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0, 6));
                      System.out.println(map);
                  }, String.valueOf(i + 1)).start();
              }
          }
      }
      
    • 11.3.2、运行结果(Run Result)

      其运行结果,如下所示

      JUC并发编程时,使用ConcurrentHashMap创建Map集合
      {1=7f2c25}
      {1=7f2c25, 5=5c4f26}
      {1=7f2c25, 5=5c4f26, 6=1f8587}
      {1=7f2c25, 5=5c4f26, 6=1f8587, 7=68d965}
      {1=7f2c25, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7}
      {1=7f2c25, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17}
      {1=7f2c25, 2=00e5f6, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17}
      {1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17}
      {1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 10=674b33}
      {11=ef664c, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 10=674b33}
      {11=ef664c, 1=7f2c25, 12=12f977, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 10=674b33}
      {11=ef664c, 12=12f977, 16=ead7ce, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 10=674b33}
      {11=ef664c, 12=12f977, 13=efb8d8, 16=ead7ce, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 10=674b33}
      {11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 10=674b33}
      {11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 17=870d1e, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 10=674b33}
      {11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 17=870d1e, 18=1bd504, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 10=674b33}
      {11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 17=870d1e, 18=1bd504, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 10=674b33}
      {11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 10=674b33}
      {11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 10=674b33, 21=c5fd62}
      {11=ef664c, 22=90cd8a, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 10=674b33, 21=c5fd62}
      {11=ef664c, 22=90cd8a, 12=12f977, 13=efb8d8, 14=6f2c78, 25=e5b769, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 10=674b33, 21=c5fd62}
      {11=ef664c, 22=90cd8a, 12=12f977, 13=efb8d8, 24=5c2995, 14=6f2c78, 25=e5b769, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 10=674b33, 21=c5fd62}
      {11=ef664c, 22=90cd8a, 12=12f977, 13=efb8d8, 24=5c2995, 14=6f2c78, 25=e5b769, 16=ead7ce, 17=870d1e, 28=8decfb, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 10=674b33, 21=c5fd62}
      {22=90cd8a, 24=5c2995, 25=e5b769, 28=8decfb, 29=37dde7, 10=674b33, 11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 21=c5fd62}
      {22=90cd8a, 24=5c2995, 25=e5b769, 28=8decfb, 29=37dde7, 30=921452, 10=674b33, 11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 21=c5fd62}
      {22=90cd8a, 24=5c2995, 25=e5b769, 28=8decfb, 29=37dde7, 30=921452, 10=674b33, 11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 15=6dab40, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 21=c5fd62}
      {22=90cd8a, 24=5c2995, 25=e5b769, 27=6371e8, 28=8decfb, 29=37dde7, 30=921452, 10=674b33, 11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 15=6dab40, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 21=c5fd62}
      {22=90cd8a, 24=5c2995, 25=e5b769, 26=9e2743, 27=6371e8, 28=8decfb, 29=37dde7, 30=921452, 10=674b33, 11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 15=6dab40, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 21=c5fd62}
      {22=90cd8a, 23=9a833e, 24=5c2995, 25=e5b769, 26=9e2743, 27=6371e8, 28=8decfb, 29=37dde7, 30=921452, 10=674b33, 11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 15=6dab40, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 21=c5fd62}
      {22=90cd8a, 23=9a833e, 24=5c2995, 25=e5b769, 26=9e2743, 27=6371e8, 28=8decfb, 29=37dde7, 30=921452, 10=674b33, 11=ef664c, 12=12f977, 13=efb8d8, 14=6f2c78, 15=6dab40, 16=ead7ce, 17=870d1e, 18=1bd504, 19=c3ae42, 1=7f2c25, 2=00e5f6, 3=b22a8f, 4=85bc22, 5=5c4f26, 6=1f8587, 7=68d965, 8=db0ea7, 9=643b17, 20=081ea6, 21=c5fd62}
      

12、Callable接口(Callable Interface)

  • 12.1、Callable接口与Runnable接口的区别(The Difference Callable Interface And Runnable Interface)

    • 12.1.1、返回结果不一样

      Callable接口的实现类可以返回结果,Runnable接口的实现类不能返回结果

    • 12.1.2、抛出异常不一样

      Callable接口的实现类可以抛出被检查的异常,Runnable接口的实现类不能抛出被检查的异常

    • 12.1.3、重写方法不一样

      Callable接口的实现类重写的是call方法,Runnable接口的实现类重写的是run方法

  • 12.2、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    /**
     * Callable接口
     * 1、Callable接口实现类对象有缓存,同一个对象在内存中只需生成一次即可
     * 2、返回结果可能会等待,从而产生阻塞
     */
    public class CallableInterface {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    //        new Thread().start();
    //        new Thread(()->{}).start();
    //        new Thread(()->{
    //            System.out.println("Runnable");
    //        }).start();
            ImplementCallable implementCallable = new ImplementCallable();//Callable接口实现类对象
            FutureTask futureTask = new FutureTask(implementCallable);//适配类对象
            new Thread(futureTask,"1").start();//Callable线程1
            new Thread(futureTask,"2").start();//Callable线程2
            FutureTask futureTask1 = new FutureTask(new ImplementCallable());
            new Thread(futureTask1,"3").start();//Callable线程3
            Integer futureTaskGet = (Integer) futureTask.get();//get方法可能会产生阻塞,一般建议放到代码最后位置
            System.out.println("futureTaskGet = " + futureTaskGet);
            Integer futureTaskGet1= (Integer) futureTask1.get();//get方法可能会产生阻塞,一般建议放到代码最后位置
            System.out.println("futureTaskGet1 = " + futureTaskGet1);
        }
    }
    //Callable接口实现类
    class ImplementCallable implements Callable<Integer>{
        @Override
        public Integer call(){
            System.out.println("Callable线程"+Thread.currentThread().getName());
            //耗时操作
            return 1024;
        }
    }
    
  • 12.3、运行结果(Run Result)

    其运行结果,如下所示

    Callable线程1
    Callable线程3
    futureTaskGet = 1024
    futureTaskGet1 = 1024
    

13、CountDownLatch类(CountDownLatch Class)

“倒计时闩锁”,即,“减法计数器”

  • 13.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc;
    import java.util.concurrent.CountDownLatch;
    /**
     * CountDownLatch类:“倒计时闩锁”,即,“减法计数器”
     */
    public class CountDownLatchClass {
        public static void main(String[] args) {
            //设置计数器数量为3,必须要执行任务时再使用
            CountDownLatch countDownLatch = new CountDownLatch(3);
            System.out.println("减法计数器设定数量:"+countDownLatch.getCount());
            for (long i = 0; i < countDownLatch.getCount(); i++) {
                new Thread(()->{
                    System.out.println(Thread.currentThread().getName()+"线程执行");
                    countDownLatch.countDown();//计数器数量-1
                    System.out.println("当前计数器数量:"+countDownLatch.getCount());
                },String.valueOf(i+1)).start();
            }
            try {
                countDownLatch.await();//等待计数器归零,然后再继续往后执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("所有线程执行完毕");
        }
    }
    
  • 13.2、运行结果(Run Result)

    其运行结果,如下所示

    减法计数器设定数量:3
    2线程执行
    1线程执行
    当前计数器数量:1
    当前计数器数量:2
    3线程执行
    当前计数器数量:0
    所有线程执行完毕
    

14、CyclicBarrier类(CyclicBarrier Class)

“循环屏障”,即,“加法计数器”

  • 14.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc;
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    /**
     * CyclicBarrier类:"循环屏障",即,“加法计数器”
     */
    public class CyclicBarrierClass {
        public static void main(String[] args) {
            //设置计数器数量为3,必须要执行任务时再使用
            CyclicBarrier cyclicBarrier = new CyclicBarrier(3,()->{
                System.out.println("所有线程执行完毕");
            });
            System.out.println("加法计数器设定数量:"+cyclicBarrier.getParties());
            for (int i = 0; i < cyclicBarrier.getParties(); i++) {
                final int partiesTemp=i+1;
                new Thread(()->{
                    System.out.println("当前计数器数量:"+partiesTemp);
                    System.out.println(Thread.currentThread().getName()+"线程执行");
                    try {
                        cyclicBarrier.await();//等待所有线程执行完毕
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                },String.valueOf(i+1)).start();
            }
        }
    }
    
  • 14.2、运行结果(Run Result)

    其运行结果,如下所示

    加法计数器设定数量:3
    当前计数器数量:1
    当前计数器数量:3
    当前计数器数量:2
    2线程执行
    3线程执行
    1线程执行
    所有线程执行完毕
    

15、Semaphore类(Semaphore Class)

“信号量”,即,“信号量计数器”

  • 15.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    /**
     * Semaphore类:"信号量",即,“信号量计数器”
     * 主要作用:多个共享资源互斥使用,并发限流,控制最大的线程数
     * 应用场景:限流(如“停车位”)
     */
    public class SemaphoreClass {
        public static void main(String[] args) {
            //设置计数器数量为5(即“5个停车位”)
            Semaphore semaphore = new Semaphore(5);
            System.out.println("信号量计数器设定数量:"+semaphore.availablePermits());
            for (int i = 0; i < 20; i++) {
                new Thread(()->{
                    try {
                        semaphore.acquire();//获得信号量-1(若信号量已满,则一直等待有释放的信号量为止)
                        System.out.println(Thread.currentThread().getName()+"号车停到车位");
                        TimeUnit.SECONDS.sleep(3);//停车时间3秒
                        System.out.println(Thread.currentThread().getName()+"号车离开车位");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally{
                        semaphore.release();//释放信号量+1(释放当前信号量,然后唤醒等待的其它线程)
                    }
                },String.valueOf(i+1)).start();
            }
        }
    }
    
  • 15.2、运行结果(Run Result)

    其运行结果,如下所示

    信号量计数器设定数量:5
    2号车停到车位
    4号车停到车位
    3号车停到车位
    1号车停到车位
    7号车停到车位
    4号车离开车位
    3号车离开车位
    1号车离开车位
    8号车停到车位
    9号车停到车位
    5号车停到车位
    2号车离开车位
    7号车离开车位
    10号车停到车位
    6号车停到车位
    5号车离开车位
    6号车离开车位
    11号车停到车位
    8号车离开车位
    12号车停到车位
    13号车停到车位
    9号车离开车位
    14号车停到车位
    10号车离开车位
    15号车停到车位
    11号车离开车位
    16号车停到车位
    12号车离开车位
    13号车离开车位
    17号车停到车位
    18号车停到车位
    14号车离开车位
    15号车离开车位
    19号车停到车位
    20号车停到车位
    16号车离开车位
    17号车离开车位
    18号车离开车位
    19号车离开车位
    20号车离开车位
    

16、ReadWriteLock接口(ReadWriteLock Interface)

即,“读写Lock锁”接口

  • 16.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    /**
     * ReadWriteLock接口
     * 两种锁:
     * 1、写锁(独占锁):一次只能被一个线程占有
     * 2、读锁(共享锁):多个线程可以同时占有
     * 三种特性:
     * 1、一个线程读,一个线程读,可以共存
     * 2、一个线程读,一个线程写,不可以共存
     * 3、一个线程写,一个线程写,不可以共存
     */
    public class ReadWriteLockInterface {
        public static void main(String[] args) {
            //UnLockCache unLockCache = new UnLockCache();//创建未加锁的自定义缓存类对象
            LockCache lockCache = new LockCache();//创建未加锁的自定义缓存类对象
            //写入
            for (int i = 0; i < 3; i++) {
                final int iWrite=i+1;
                new Thread(()->{
                    //unLockCache.put(String.valueOf(iWrite),String.valueOf(iWrite));
                    lockCache.put(String.valueOf(iWrite),String.valueOf(iWrite));
                },String.valueOf(iWrite)).start();
            }
            //读取
            for (int i = 0; i < 3; i++) {
                final int iRead=i+1;
                new Thread(()->{
                    //unLockCache.get(String.valueOf(iRead));
                    lockCache.get(String.valueOf(iRead));
                },String.valueOf(iRead)).start();
            }
        }
    }
    //加锁的自定义缓存类:安全,可以保证先写入再读取
    class LockCache{
        private volatile Map<String,Object> map=new HashMap<>();
        //读写Lock锁:可以更加细腻度的控制读写操作
        private ReadWriteLock readWriteLock =new ReentrantReadWriteLock();
        //存,写:同时只有一个线程可以写入
        public void put(String key,Object value){
            readWriteLock.writeLock().lock();//加锁
            try {
                System.out.println(Thread.currentThread().getName()+"写入"+key+"开始");
                map.put(key, value);
                System.out.println(Thread.currentThread().getName()+"写入结果:Key:"+key+",Value:"+value);
                System.out.println(Thread.currentThread().getName()+"写入"+key+"结束");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readWriteLock.writeLock().unlock();//解锁
            }
        }
        //取,读:同时所有线程都可以读取
        public void get(String key){
            readWriteLock.readLock().lock();//加锁
            try {
                System.out.println(Thread.currentThread().getName()+"读取"+key+"开始");
                String value = (String) map.get(key);
                System.out.println(Thread.currentThread().getName()+"读取结果:Key:"+key+",Value:"+value);
                System.out.println(Thread.currentThread().getName()+"读取"+key+"结束");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                readWriteLock.readLock().unlock();//解锁
            }
        }
    }
    //未加锁的自定义缓存类:不安全,无法保证先写入再读取
    class UnLockCache{
        private volatile Map<String,Object> map=new HashMap<>();
        //存,写
        public void put(String key,Object value){
            System.out.println(Thread.currentThread().getName()+"写入"+key+"开始");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+"写入结果:Key:"+key+",Value:"+value);
            System.out.println(Thread.currentThread().getName()+"写入"+key+"结束");
        }
        //取,读
        public void get(String key){
            System.out.println(Thread.currentThread().getName()+"读取"+key+"开始");
            String value = (String) map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取结果:Key:"+key+",Value:"+value);
            System.out.println(Thread.currentThread().getName()+"读取"+key+"结束");
        }
    }
    
  • 16.2、运行结果(Run Result)

    其运行结果,如下所示

    1写入1开始
    1写入结果:Key:1,Value:1
    1写入1结束
    2写入2开始
    2写入结果:Key:2,Value:2
    2写入2结束
    3写入3开始
    3写入结果:Key:3,Value:3
    3写入3结束
    1读取1开始
    3读取3开始
    2读取2开始
    1读取结果:Key:1,Value:1
    3读取结果:Key:3,Value:3
    1读取1结束
    3读取3结束
    2读取结果:Key:2,Value:2
    2读取2结束
    

17、BlockingQueue接口(BlockingQueue Interface)

即,“阻塞队列”接口。其主要用于“多线程并发处理(线程池)”

  • 17.1、队列(Queue)

    "队列"的原理是先入先出(First Input First Output,FIFO),若队列已满,则写入时必须阻塞等待移除;若队列为空,则读取时必须阻塞等待添加

    • 17.1.1、BlockingQueue(阻塞队列)
      • 17.1.1.1、ArrayBlockingQueue(数组阻塞队列)
      • 17.1.1.2、LinkedBlockingQueue(链表阻塞队列)
    • 17.1.2、AbstractQueue(非阻塞队列)
    • 17.1.3、Deque(双端队列)
  • 17.2、四组API(Four Groups API)

    其四组API,如下表所示

    方法第1组API:抛出异常第2组API:不抛出异常,有返回值第3组API:阻塞等待第4组API:超时等待
    添加boolean add(E e)boolean offer(E e)void put(E e)boolean offer(E e, long timeout, TimeUnit unit)
    移除E remove()E poll()E take()E poll(long timeout, TimeUnit unit)
    检测队首元素E element()E peek()
  • 17.3、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.queue;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.TimeUnit;
    /**
     * 阻塞队列接口
     * 四组API:
     * 1、抛出异常
     * 2、不抛出异常,有返回值
     * 3、阻塞等待
     * 4、超时等待
     */
    public class BlockingQueueInterface {
        public static void main(String[] args) {
            groupAPI1();//调用第1组API方法
            groupAPI2();//调用第2组API方法
            groupAPI3();//调用第3组API方法
            groupAPI4();//调用第4组API方法
        }
        //第1组API:抛出异常
        public static void groupAPI1(){
            System.out.println("第1组API");
            System.out.println("--------------------------------------------------");
            //队列大小
            ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
            System.out.println(arrayBlockingQueue.add("1"));
            System.out.println(arrayBlockingQueue.add("2"));
            System.out.println(arrayBlockingQueue.add("3"));
            //System.out.println(arrayBlockingQueue.add("4"));//抛出"非法状态"异常"IllegalStateException"
            System.out.println("阻塞队列首元素:"+arrayBlockingQueue.element());
            System.out.println(arrayBlockingQueue.remove());
            System.out.println(arrayBlockingQueue.remove());
            System.out.println(arrayBlockingQueue.remove());
            //System.out.println(arrayBlockingQueue.remove());//抛出"没有这样元素"异常"NoSuchElementException"
            //System.out.println("阻塞队列首元素:"+arrayBlockingQueue.element());//抛出"没有这样元素"异常"NoSuchElementException"
            System.out.println("--------------------------------------------------");
        }
        //第2组API:不抛出异常,有返回值
        public static void groupAPI2(){
            System.out.println("第2组API");
            System.out.println("--------------------------------------------------");
            //队列大小
            ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
            System.out.println(arrayBlockingQueue.offer("1"));
            System.out.println(arrayBlockingQueue.offer("2"));
            System.out.println(arrayBlockingQueue.offer("3"));
            System.out.println(arrayBlockingQueue.offer("4"));//不抛出异常,返回"false"
            System.out.println("阻塞队列首元素:"+arrayBlockingQueue.peek());
            System.out.println(arrayBlockingQueue.poll());
            System.out.println(arrayBlockingQueue.poll());
            System.out.println(arrayBlockingQueue.poll());
            System.out.println(arrayBlockingQueue.poll());//不抛出异常,返回"null"
            System.out.println("阻塞队列首元素:"+arrayBlockingQueue.peek());
            System.out.println("--------------------------------------------------");
        }
        //第3组API:阻塞等待
        public static void groupAPI3(){
            System.out.println("第3组API");
            System.out.println("--------------------------------------------------");
            //队列大小
            ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
            try {
                arrayBlockingQueue.put("1");
                arrayBlockingQueue.put("2");
                arrayBlockingQueue.put("3");
                //arrayBlockingQueue.put("4");//队列已满时,则一直阻塞等待
                System.out.println(arrayBlockingQueue.take());
                System.out.println(arrayBlockingQueue.take());
                System.out.println(arrayBlockingQueue.take());
                //System.out.println(arrayBlockingQueue.take());//队列为空时,则一直阻塞等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("--------------------------------------------------");
        }
        //第4组API:超时等待
        public static void groupAPI4(){
            System.out.println("第4组API");
            System.out.println("--------------------------------------------------");
            //队列大小
            ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
            try {
                System.out.println(arrayBlockingQueue.offer("1"));
                System.out.println(arrayBlockingQueue.offer("2"));
                System.out.println(arrayBlockingQueue.offer("3"));
                System.out.println(arrayBlockingQueue.offer("4",3, TimeUnit.SECONDS));//等待超过3秒时,则退出继续执行后面操作
                System.out.println(arrayBlockingQueue.poll());
                System.out.println(arrayBlockingQueue.poll());
                System.out.println(arrayBlockingQueue.poll());
                System.out.println(arrayBlockingQueue.poll(3,TimeUnit.SECONDS));//等待超过3秒时,则退出继续执行后面操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("--------------------------------------------------");
        }
    }
    
  • 17.4、运行结果(Run Result)

    其运行结果,如下所示

    1组API
    --------------------------------------------------
    true
    true
    true
    阻塞队列首元素:1
    1
    2
    3
    --------------------------------------------------2组API
    --------------------------------------------------
    true
    true
    true
    false
    阻塞队列首元素:1
    1
    2
    3
    null
    阻塞队列首元素:null
    --------------------------------------------------3组API
    --------------------------------------------------
    1
    2
    3
    --------------------------------------------------4组API
    --------------------------------------------------
    true
    true
    true
    false
    1
    2
    3
    null
    --------------------------------------------------
    

18、SynchronousQueue类(SynchronousQueue Class)

即,“同步队列”类

同步队列不存储元素(即,没有容量),且每添加(put)一个元素后必须等待移除(take)后才能继续添加(put)下一个元素。若添加(put)元素后没有移除(take)元素,则线程会一直阻塞等待,若未添加(put)元素而直接移除(take)元素,则线程也会一直阻塞等待

  • 18.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.queue;
    import java.util.concurrent.SynchronousQueue;
    import java.util.concurrent.TimeUnit;
    /**
     * 同步队列类
     * 同步队列不存储元素(即,没有容量),且每添加(put)一个元素后必须等待移除(take)后才能继续添加(put)下一个元素
     * 若添加元素后没有移除元素,则线程会一直阻塞等待,若未添加元素而直接移除元素,则线程也会一直阻塞等待
     */
    public class SynchronousQueueClass {
        public static void main(String[] args) {
            SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();//同步队列
            //添加同步队列元素
            new Thread(()->{
                try {
                    synchronousQueue.put("1");
                    System.out.println(Thread.currentThread().getName()+"添加同步队列元素1");
                    synchronousQueue.put("2");
                    System.out.println(Thread.currentThread().getName()+"添加同步队列元素2");
                    synchronousQueue.put("3");
                    System.out.println(Thread.currentThread().getName()+"添加同步队列元素3");
    //                synchronousQueue.put("4");
    //                System.out.println(Thread.currentThread().getName()+"添加同步队列元素4");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"1").start();
            //移除同步队列元素
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(3);//延时3秒
                    System.out.println(Thread.currentThread().getName()+"移除同步队列元素"+synchronousQueue.take());
                    TimeUnit.SECONDS.sleep(3);//延时3秒
                    System.out.println(Thread.currentThread().getName()+"移除同步队列元素"+synchronousQueue.take());
                    TimeUnit.SECONDS.sleep(3);//延时3秒
                    System.out.println(Thread.currentThread().getName()+"移除同步队列元素"+synchronousQueue.take());
    //                TimeUnit.SECONDS.sleep(3);//延时3秒
    //                System.out.println(Thread.currentThread().getName()+"移除同步队列元素"+synchronousQueue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"2").start();
        }
    }
    
  • 18.2、运行结果(Run Result)

    其运行结果,如下所示

    1添加同步队列元素1
    2移除同步队列元素1
    2移除同步队列元素2
    1添加同步队列元素2
    1添加同步队列元素3
    2移除同步队列元素3
    

19、线程池(Thread Pool)

线程池使用“池化技术”来实现,且其分为:“三大方法、七大参数、四种拒绝策略”

  • 19.1、线程池优点(Thread Pool Advantages)

    • 19.1.1、可以控制线程复用(即,不需要频繁的创建与销毁资源对象),从而降低资源的消耗
    • 19.1.2、可以控制最大并发数,从而提高响应的速度
    • 19.1.3、可以更安全与方便的管理线程
  • 19.2、源码分析(Source Code Analysis)

    线程池的Executors工具类三大方法源码:
    '不建议使用Executors工具类创建线程池,可能会创建大量线程或堆积大量请求,从而导致异常OOM(Out Of Memory,内存溢出)',而是'建议使用原始ThreadPoolExecutor类ThreadPoolExecutor方法来创建线程池'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>());
    }
    'Executors工具类三大方法的本质''调用源码ThreadPoolExecutor方法'。
    
    线程池的ThreadPoolExecutorThreadPoolExecutor方法中七大参数源码:
    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;
    }
    
    线程池四种拒绝策略源码:
    public static class AbortPolicy implements RejectedExecutionHandler {
        public AbortPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }
    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        public CallerRunsPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }
    public static class DiscardPolicy implements RejectedExecutionHandler {
        public DiscardPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        public DiscardOldestPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }
    
  • 19.3、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.threadpool;
    import java.util.concurrent.*;
    /**
     * 线程池:三大方法、七大参数、四种拒绝策略
     * Executors工具类的三大方法:
     * 1、Executors.newSingleThreadExecutor();//单个线程的线程池
     * 2、Executors.newFixedThreadPool(int nThreads);//固定大小的线程池
     * 3、Executors.newCachedThreadPool();//缓存线程池(线程可复用)
     * ThreadPoolExecutor类ThreadPoolExecutor方法中七大参数:
     * 1、int corePoolSize//核心线程池大小
     * 2、int maximumPoolSize//最大线程池大小
     * 3、long keepAliveTime//超时等待时间大小
     * 4、TimeUnit unit//超时等待时间单位
     * 5、BlockingQueue<Runnable> workQueue//阻塞队列
     * 6、ThreadFactory threadFactory//线程工厂(创建线程,一般不用改变)
     * 7、RejectedExecutionHandler handler//拒绝策略
     * 四种拒绝策略:
     * 1、实现RejectedExecutionHandler接口的AbortPolicy类:若队列已满,还有线程请求进入,则不处理此线程且抛出异常
     * 2、实现RejectedExecutionHandler接口的CallerRunsPolicy类:若队列已满,还有线程请求进入,则退回执行此线程的调用者(即,哪里来的回哪里去)
     * 3、实现RejectedExecutionHandler接口的DiscardPolicy类:若队列已满,还有线程请求进入,则丢掉此线程且不抛出异常
     * 4、实现RejectedExecutionHandler接口的DiscardOldestPolicy类:若队列已满,还有线程请求进入,则尝试和最早进入的线程竞争线程池资源且不抛出异常
     * CPU密集型和IO密集型主要是为了调整优化资源的分配
     * 1、CPU密集型:最大线程池数=CPU的核数
     * 2、IO密集型:程序中非常耗IO的线程数<最大线程池数<程序中很耗IO的线程数的2倍
     */
    public class ThreadPool {
        public static void main(String[] args) {
            try {
                //使用Executors工具类的三大方法来创建线程池
                ExecutorsMethod1();//单个线程的线程池
                TimeUnit.SECONDS.sleep(1);//延时1秒
                ExecutorsMethod2();//固定大小的线程池
                TimeUnit.SECONDS.sleep(2);//延时2秒
                ExecutorsMethod3();//缓存线程池(线程可复用)
                TimeUnit.SECONDS.sleep(3);//延时3秒
                //自定义原始ThreadPoolExecutor类ThreadPoolExecutor方法来创建线程池
                threadPoolExecutor1();//自定义线程池:使用实现RejectedExecutionHandler接口的AbortPolicy类的拒绝策略
                TimeUnit.SECONDS.sleep(3);//延时3秒
                threadPoolExecutor2();//自定义线程池:使用实现RejectedExecutionHandler接口的CallerRunsPolicy类
                TimeUnit.SECONDS.sleep(3);//延时3秒
                threadPoolExecutor3();//自定义线程池:使用实现RejectedExecutionHandler接口的DiscardPolicy类
                TimeUnit.SECONDS.sleep(3);//延时3秒
                threadPoolExecutor4();//自定义线程池:使用实现RejectedExecutionHandler接口的DiscardOldestPolicy类
                TimeUnit.SECONDS.sleep(3);//延时3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void ExecutorsMethod1(){
            ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程的线程池
            for (int i = 0; i < 10; i++) {
                //使用线程池创建线程
                threadPool.execute(()->{
                    System.out.println((Thread.currentThread().getName()));
                });
            }
            threadPool.shutdown();//关闭线程池
        }
        public static void ExecutorsMethod2(){
            ExecutorService threadPool = Executors.newFixedThreadPool(3);//固定大小的线程池
            for (int i = 0; i < 10; i++) {
                //使用线程池创建线程
                threadPool.execute(()->{
                    System.out.println((Thread.currentThread().getName()));
                });
            }
            threadPool.shutdown();//关闭线程池
        }
        public static void ExecutorsMethod3(){
            ExecutorService threadPool = Executors.newCachedThreadPool();//缓存线程池(线程可复用)
            for (int i = 0; i < 10; i++) {
                //使用线程池创建线程
                threadPool.execute(()->{
                    System.out.println((Thread.currentThread().getName()));
                });
            }
            threadPool.shutdown();//关闭线程池
        }
        public static void threadPoolExecutor1(){
            /**
             * 自定义线程池:使用实现RejectedExecutionHandler接口的AbortPolicy类的拒绝策略
             * 在线程池中,最大线程到底该如何定义?
             * 1、CPU密集型:最大线程池数=CPU的核数
             * 2、IO密集型:程序中非常耗IO的线程数<最大线程池数<程序中很耗IO的线程数的2倍
             */
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,Runtime.getRuntime().availableProcessors(), 1,TimeUnit.SECONDS,new LinkedBlockingDeque<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
            /**
             * 最大承载数:LinkedBlockingDeque容量+最大线程池数量
             * 超过最大承载数时,会抛出异常“RejectedExecutionException”
             */
            try {
                int QueueCapacity=threadPoolExecutor.getQueue().remainingCapacity();
                for (int i = 0; i < threadPoolExecutor.getMaximumPoolSize()+QueueCapacity+30; i++) {
                    //使用线程池创建线程
                    threadPoolExecutor.execute(()->{
                        System.out.println(Thread.currentThread().getName());
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                threadPoolExecutor.shutdown();//关闭线程池
            }
        }
        public static void threadPoolExecutor2(){
            /**
             * 自定义线程池:使用实现RejectedExecutionHandler接口的CallerRunsPolicy类
             * 在线程池中,最大线程到底该如何定义?
             * 1、CPU密集型:最大线程池数=CPU的核数
             * 2、IO密集型:程序中非常耗IO的线程数<最大线程池数<程序中很耗IO的线程数的2倍
             */
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,Runtime.getRuntime().availableProcessors(), 1,TimeUnit.SECONDS,new LinkedBlockingDeque<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
            /**
             * 最大承载数:LinkedBlockingDeque容量+最大线程池数量
             */
            int QueueCapacity=threadPoolExecutor.getQueue().remainingCapacity();
            for (int i = 0; i < threadPoolExecutor.getMaximumPoolSize()+QueueCapacity+30; i++) {
                //使用线程池创建线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
            threadPoolExecutor.shutdown();//关闭线程池
        }
        public static void threadPoolExecutor3(){
            /**
             * 自定义线程池:使用实现RejectedExecutionHandler接口的DiscardPolicy类
             * 在线程池中,最大线程到底该如何定义?
             * 1、CPU密集型:最大线程池数=CPU的核数
             * 2、IO密集型:程序中非常耗IO的线程数<最大线程池数<程序中很耗IO的线程数的2倍
             */
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,Runtime.getRuntime().availableProcessors(), 1,TimeUnit.SECONDS,new LinkedBlockingDeque<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());
            /**
             * 最大承载数:LinkedBlockingDeque容量+最大线程池数量
             */
            int QueueCapacity=threadPoolExecutor.getQueue().remainingCapacity();
            for (int i = 0; i < threadPoolExecutor.getMaximumPoolSize()+QueueCapacity+30; i++) {
                //使用线程池创建线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
            threadPoolExecutor.shutdown();//关闭线程池
        }
        public static void threadPoolExecutor4(){
            /**
             * 自定义线程池:使用实现RejectedExecutionHandler接口的DiscardOldestPolicy类
             * 在线程池中,最大线程到底该如何定义?
             * 1、CPU密集型:最大线程池数=CPU的核数
             * 2、IO密集型:程序中非常耗IO的线程数<最大线程池数<程序中很耗IO的线程数的2倍
             */
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,Runtime.getRuntime().availableProcessors(), 1,TimeUnit.SECONDS,new LinkedBlockingDeque<>(5),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());
            /**
             * 最大承载数:LinkedBlockingDeque容量+最大线程池数量
             */
            int QueueCapacity=threadPoolExecutor.getQueue().remainingCapacity();
            for (int i = 0; i < threadPoolExecutor.getMaximumPoolSize()+QueueCapacity+30; i++) {
                //使用线程池创建线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
            threadPoolExecutor.shutdown();//关闭线程池
        }
    }
    
  • 19.4、运行结果(Run Result)

    其运行结果,如下所示

    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-1-thread-1
    pool-2-thread-1
    pool-2-thread-2
    pool-2-thread-2
    pool-2-thread-1
    pool-2-thread-2
    pool-2-thread-1
    pool-2-thread-3
    pool-2-thread-2
    pool-2-thread-3
    pool-2-thread-1
    pool-3-thread-1
    pool-3-thread-2
    pool-3-thread-2
    pool-3-thread-1
    pool-3-thread-3
    pool-3-thread-4
    pool-3-thread-5
    pool-3-thread-3
    pool-3-thread-4
    pool-3-thread-5
    pool-4-thread-2
    pool-4-thread-1
    pool-4-thread-3
    pool-4-thread-1
    pool-4-thread-1
    pool-4-thread-3
    pool-4-thread-3
    pool-4-thread-3
    pool-4-thread-3
    pool-4-thread-4
    pool-4-thread-4
    pool-4-thread-4
    pool-4-thread-1
    pool-4-thread-4
    pool-4-thread-4
    pool-4-thread-5
    pool-4-thread-5
    pool-4-thread-5
    pool-4-thread-1
    pool-4-thread-1
    pool-4-thread-1
    pool-4-thread-1
    pool-4-thread-5
    pool-4-thread-5
    pool-4-thread-5
    pool-4-thread-5
    pool-4-thread-5
    pool-4-thread-5
    pool-4-thread-8
    pool-4-thread-8
    pool-4-thread-8
    pool-4-thread-8
    pool-4-thread-8
    pool-4-thread-2
    pool-4-thread-6
    pool-4-thread-7
    java.util.concurrent.RejectedExecutionException: Task com.xueshanxuehai.juc.threadpool.ThreadPool$$Lambda$23/0x0000000800c03218@5b6f7412 rejected from java.util.concurrent.ThreadPoolExecutor@27973e9b[Running, pool size = 8, active threads = 4, queued tasks = 1, completed tasks = 30]
    	at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2057)
    	at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:827)
    	at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1357)
    	at com.xueshanxuehai.juc.threadpool.ThreadPool.threadPoolExecutor1(ThreadPool.java:97)
    	at com.xueshanxuehai.juc.threadpool.ThreadPool.main(ThreadPool.java:39)
    pool-5-thread-1
    pool-5-thread-2
    pool-5-thread-2
    pool-5-thread-2
    pool-5-thread-2
    pool-5-thread-3
    pool-5-thread-3
    pool-5-thread-1
    pool-5-thread-1
    pool-5-thread-1
    pool-5-thread-1
    pool-5-thread-3
    pool-5-thread-5
    pool-5-thread-5
    pool-5-thread-4
    pool-5-thread-2
    pool-5-thread-3
    pool-5-thread-1
    pool-5-thread-4
    pool-5-thread-4
    pool-5-thread-4
    pool-5-thread-4
    pool-5-thread-4
    pool-5-thread-4
    pool-5-thread-4
    pool-5-thread-4
    pool-5-thread-5
    pool-5-thread-3
    pool-5-thread-3
    pool-5-thread-3
    pool-5-thread-5
    pool-5-thread-4
    pool-5-thread-2
    pool-5-thread-8
    pool-5-thread-5
    pool-5-thread-3
    pool-5-thread-1
    main
    pool-5-thread-4
    pool-5-thread-4
    pool-5-thread-6
    pool-5-thread-1
    pool-5-thread-7
    pool-6-thread-1
    pool-6-thread-2
    pool-6-thread-3
    pool-6-thread-1
    pool-6-thread-1
    pool-6-thread-1
    pool-6-thread-1
    pool-6-thread-2
    pool-6-thread-2
    pool-6-thread-2
    pool-6-thread-2
    pool-6-thread-2
    pool-6-thread-2
    pool-6-thread-2
    pool-6-thread-2
    pool-6-thread-5
    pool-6-thread-3
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-5
    pool-6-thread-2
    pool-6-thread-4
    pool-6-thread-1
    pool-6-thread-5
    pool-6-thread-8
    pool-6-thread-3
    pool-6-thread-6
    pool-6-thread-7
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-1
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-3
    pool-7-thread-4
    pool-7-thread-1
    pool-7-thread-1
    pool-7-thread-1
    pool-7-thread-5
    pool-7-thread-4
    pool-7-thread-2
    pool-7-thread-1
    pool-7-thread-1
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-3
    pool-7-thread-2
    pool-7-thread-2
    pool-7-thread-1
    pool-7-thread-4
    pool-7-thread-5
    pool-7-thread-8
    pool-7-thread-3
    pool-7-thread-6
    pool-7-thread-7
    

20、四大函数式接口(Four Functional Interface)

函数式接口,即“只有一个方法的接口”

四大函数式接口:“Function接口、Predicate接口、Consumer接口、Supplier接口”

20.1、Function接口(Function Interface)

Function接口,即“函数型接口”

  • 20.1.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.functionalinterface;
    import java.util.function.Function;
    /**
     * Function接口:即,“函数型接口”
     * 其有一个输入参数,有返回参数
     */
    public class FunctionInterface {
        public static void main(String[] args) {
            System.out.println("Function接口:即,\"函数型接口\",其有一个输入参数,有返回参数");
    //        Function<String, String> function = new Function<String,String>() {
    //            //匿名内部类
    //            @Override
    //            public String apply(String str) {
    //                return str;
    //            }
    //        };
            Function<String,String> function=(str)->{return str;};//Labmda表达式
    //        Function<String,String> function=str->{return str;};//Labmda表达式(最简化)
            System.out.println(function.apply("123"));
        }
    }
    
  • 20.1.2、运行结果(Run Result)

    其运行结果,如下所示

    Function接口:即,"函数型接口",其有一个输入参数,有返回参数
    123
    

20.2、Predicate接口(Predicate Interface)

Predicate接口,即“断定型接口”

  • 20.2.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.functionalinterface;
    import java.util.function.Predicate;
    /**
     * Predicate接口:即,“断定型接口”
     * 其有一个输入参数,且返回参数只能是布尔型
     */
    public class PredicateInterface {
        public static void main(String[] args) {
            System.out.println("Predicate接口:即,\"断定型接口\",其有一个输入参数,且返回参数只能是布尔型");
    //        Predicate<String> predicate = new Predicate<String>() {
    //            //匿名内部类
    //            @Override
    //            public boolean test(String str) {
    //                return str.isEmpty();
    //            }
    //        };
            Predicate<String> predicate=(str)->{return str.isEmpty();};//Labmda表达式
    //        Predicate<String> predicate=str->{return str.isEmpty();};//Labmda表达式(最简化)
            System.out.println(predicate.test(""));
        }
    }
    
  • 20.2.2、运行结果(Run Result)

    其运行结果,如下所示

    Predicate接口:即,"断定型接口",其有一个输入参数,且返回参数只能是布尔型
    true
    

20.3、Consumer接口(Consumer Interface)

Consumer接口,即“消费型接口”

  • 20.3.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.functionalinterface;
    import java.util.function.Consumer;
    /**
     * Consumer接口:即,“消费型接口”
     * 其只有一个输入参数,没有返回参数
     */
    public class ConsumerInterface {
        public static void main(String[] args) {
            System.out.println("Consumer接口:即,\"消费型接口\",其只有一个输入参数,没有返回参数");
    //        Consumer<String> consumer = new Consumer<String>() {
    //            //匿名内部类
    //            @Override
    //            public void accept(String str) {
    //                System.out.println(str);
    //            }
    //        };
            Consumer<String> consumer=(str)->{System.out.println(str);};//Lambda表达式
    //        Consumer<String> consumer=str->System.out.println(str);//Lambda表达式(最简化)
            consumer.accept("456");
        }
    }
    
  • 20.3.2、运行结果(Run Result)

    其运行结果,如下所示

    Consumer接口:即,"消费型接口",其只有一个输入参数,没有返回参数
    456
    

20.4、Supplier接口(Supplier Interface)

Supplier接口,即“供给型接口”

  • 20.4.1、示例代码(Smaple Code)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.functionalinterface;
    import java.util.function.Supplier;
    /**
     * Supplier接口:即,“供给型接口”
     * 其没有输入参数,只有返回参数
     */
    public class SupplierInterface {
        public static void main(String[] args) {
            System.out.println("Supplier接口:即,\"供给型接口\",其没有输入参数,只有返回参数");
    //        Supplier<Integer> supplier = new Supplier<Integer>() {
    //            //匿名内部类
    //            @Override
    //            public Integer get() {
    //                return 1024;
    //            }
    //        };
            Supplier<Integer> supplier=()->{return 1024;};//Lambda表达式(最简化)
            System.out.println(supplier.get());
        }
    }
    
  • 20.4.2、运行结果(Run Result)

    其运行结果,如下所示

    Supplier接口:即,"供给型接口",其没有输入参数,只有返回参数
    1024
    

21、Stream流式计算(Stream Flow Calculation)

从JDK1.8开始,之前需要多行代码才能完成的功能,使用“流式计算”后可在一行代码中实现此功能

大数据:“存储+计算”,其中“存储”一般使用“集合、MySQL”,而“计算”都应该使用"流"来处理

  • 21.1、示例代码(Smaple Code)

其示例代码,如下所示

package com.xueshanxuehai.juc;
import com.xueshanxuehai.io.javabean.User;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
 * Stream流式计算
 * 题目要求:一分钟内完成此题,只能用一行代码实现。
 * 现在有5个用户,实现以下筛选条件。
 * 1、ID必须是偶数;
 * 2、年龄必须大于23岁;
 * 3、用户名转为大写字母;
 * 4、用户名字母逆序排序;
 * 5、只输出一个用户;
 */
public class StreamFlowCalculation{
    public static void main(String[] args) {
        Users u1 = new Users(0, "a", 24);
        Users u2 = new Users(1, "b", 20);
        Users u3 = new Users(2, "c", 26);
        Users u4 = new Users(3, "d", 22);
        Users u5 = new Users(4, "e", 28);
        List<Users> list= Arrays.asList(u1,u2,u3,u4,u5);//集合
        list.stream()
                .filter(u->{return u.getId()%2==0;})//筛选偶数ID
                .filter(u->{return u.getAge()>23;})//筛选年龄必须大于23岁
                .map(u->{return u.getName().toUpperCase();})//用户名转为大写字母
                .sorted((c1,c2)->{return c2.compareTo(c1);})//用户名字母逆序排序
                .limit(1)//只输出一个用户
                .forEach(System.out::println);//遍历Stream流中的元素
    }
}
//用户类
class Users{
    private int id;
    private String name;
    private int age;
    public Users() {
    }
    public Users(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Users users = (Users) o;
        return id == users.id && age == users.age && Objects.equals(name, users.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(id, name, age);
    }
    @Override
    public String toString() {
        return "Users{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 21.2、运行结果(Run Result)

    其运行结果,如下所示

E

22、ForkJoin详解(ForkJoin Detail Explain)

从JDK1.7开始,处理大数据时可以使用“ForkJoin”并发执行任务,从而提高效率

“ForkJoin”:即,拆分合并或拆分汇总。亦即,把大任务拆分(Fork)成若干个小任务(直到不可再拆分),再将拆分后所有小任务的运算结果进行合并或汇总(Join)

“ForkJoin”特点:在双端队列中,使用递归的原理进行“工作窃取”

  • 22.1、示例代码(Smaple Code)

ForkJoin类,其代码如下所示

package com.xueshanxuehai.juc.forkjoin;
import java.util.concurrent.RecursiveTask;
/**
 * ForkJoin:实现求和计算的任务。
 * 1、实现ForkJoin功能的类要继承ForkJoinTask类或其子类
 * 2、创建ForkJoinPool对象实例
 * 3.1、执行ForkJoinPool对象实例的execute(ForkJoinTask<?> task)方法提交计算任务,无返回值
 * 3.2、执行ForkJoinPool对象实例的submit(ForkJoinTask<?> task)方法提交计算任务,有返回值(推荐使用)
 */
public class ForkJoin extends RecursiveTask<Long> {
    private long st;//开始值
    private long ed;//结束值
    private long temp=1_0000L;//临界值
    public ForkJoin() {
    }
    public ForkJoin(long st, long ed) {
        this.st = st;
        this.ed = ed;
    }
    //重写compute方法
    @Override
    protected Long compute() {
        if((ed-st)<temp){
            long sum=0L;
            for (long i = st; i <=ed; i++) {
                sum+=i;
            }
            return sum;
        }else{//forkjoin:拆分合并或汇总,递归原理
            long middle=(st+ed)/2;//中间值
            ForkJoin forkJoinTask1 = new ForkJoin(st,middle);
            forkJoinTask1.fork();//拆分子任务1,把子任务1放入线程队列中
            ForkJoin forkJoinTask2 = new ForkJoin(middle+1, ed);
            forkJoinTask2.fork();//拆分子任务2,把子任务2放入线程队列中
            return forkJoinTask1.join()+forkJoinTask2.join();//合并或汇总子任务1与子任务2的运算结果
        }
    }
}

Test类,其示例代码,如下所示

package com.xueshanxuehai.juc.forkjoin;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
 * 实现求和计算的3种方式:
 * 1、普通求和(低级程序员)
 * 2、ForkJoin求和(中级程序员)
 * 3、Stream并行流求和(高级程序员)
 */
public class Test {
    public static void main(String[] args) {
        way1();//方式1:普通求和
        way2();//方式2:ForkJoin求和
        way3();//方式3:Stream并行流求和
    }
    //方式1:普通求和
    public static void way1(){
        long st_DT=System.currentTimeMillis();//开始计算时间(毫秒)
        long sum=0L;
        for (long i = 1L; i <= 10_0000_0000L; i++) {
            sum+=i;
        }
        long ed_DT=System.currentTimeMillis();//结束计算时间(毫秒)
        System.out.println("使用“普通”求和的运算结果:" + sum+",花费时间:"+(ed_DT-st_DT)+"毫秒");
    }
    //方式2:ForkJoin求和
    public static void way2(){
        long st_DT=System.currentTimeMillis();//开始计算时间(毫秒)
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> forkJoinTask = new ForkJoin(1L,10_0000_0000L);
        //forkJoinPool.execute(forkJoinTask);//提交任务,无返回值
        ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinTask);//提交任务,有返回值(推荐使用)
        long sum= 0L;//获取任务的运算结果
        try {
            sum = submit.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        long ed_DT=System.currentTimeMillis();//结束计算时间(毫秒)
        System.out.println("使用“ForkJoin”求和的运算结果:" + sum+",花费时间:"+(ed_DT-st_DT)+"毫秒");
    }
    //方式3:Stream并行流求和
    public static void way3(){
        long st_DT=System.currentTimeMillis();//开始计算时间(毫秒)
        long sum= LongStream.rangeClosed(1L,10_0000_0000L).parallel().reduce(0,Long::sum);
        long ed_DT=System.currentTimeMillis();//结束计算时间(毫秒)
        System.out.println("使用“Stream并行流”求和的运算结果:" + sum+",花费时间:"+(ed_DT-st_DT)+"毫秒");
    }
}
  • 22.2、运行结果(Run Result)

    其运行结果,如下所示

使用“普通”求和的运算结果:500000000500000000,花费时间:345毫秒
使用“ForkJoin”求和的运算结果:500000000500000000,花费时间:200毫秒
使用“Stream并行流”求和的运算结果:500000000500000000,花费时间:116毫秒

23、异步回调(Asynchronous Callback)

Future设计的初衷:“对未来某个事件的结果进行建模”

  • 23.1、示例代码(Smaple Code)

其示例代码,如下所示

package com.xueshanxuehai.juc;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
 * 异步回调:
 * 1、CompletableFuture类的runAsync()方法:异步执行,无返回值:
 * 2、CompletableFuture类的supplyAsync()方法:异步执行,有返回值:
 */
public class AsynchronousCallback {
    public static void main(String[] args) {
        //无返回值的异步回调:runAsync()方法
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"无返回值的异步回调:runAsync()方法");
        });
        System.out.println("123");
        try {
            System.out.println(voidCompletableFuture.get());//获取阻塞执行结果
        } catch (Exception e) {
            e.printStackTrace();
        }
        /**
         * 有返回值的异步回调:supplyAsync()方法
         * 类似于ajax
         * 成功与失败的回调
         */
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName()+"有返回值的异步回调:supplyAsync()方法");
            int i=0/0;//异常代码(测试)
            return 1024;
        });
        System.out.println("456");
        try {
            System.out.println(integerCompletableFuture.whenComplete((t, u) -> {
                System.out.println("t = " + t);//正常的返回结果
                System.out.println("u = " + u);//异常错误信息
            }).exceptionally((e) -> {
                System.out.println("e.getMessage() = " + e.getMessage());
                return 444;//错误的返回结果
            }).get());//获取阻塞执行结果
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 23.2、运行结果(Run Result)

    其运行结果,如下所示

123
ForkJoinPool.commonPool-worker-3无返回值的异步回调:runAsync()方法
null
456
ForkJoinPool.commonPool-worker-3有返回值的异步回调:supplyAsync()方法
t = null
u = java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
e.getMessage() = java.lang.ArithmeticException: / by zero
444

24、理解JMM(Understand JMM)

JMM,即“Java内存模型(Java Memory Model)”,只是一种概念或约定,不是一种具体存在的东西

JMM的3大特征:“1、原子性(Atomicity);2、可见性(Visibility);3、有序性(Order)”

JMM规定了所有的变量都存储在主内存(Main Memory)中。每个线程还有自己的工作内存(Working Memory),线程的工作内存中保存了该线程使用到的变量的主内存的副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量(volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序性规定,所以看起来如同直接在主内存中读写访问一般)。不同的线程之间也无法直接访问对方工作内存中的变量,线程之间值的传递都需要通过主内存来完成

  • 24.1、JMM同步约定(JMM Synchronization Convention)

    • 24.1.1、线程加锁与解锁必须是同一把锁
    • 24.1.2、线程加锁前,必须读取主内存中的共享变量最新值到工作内存中
    • 24.1.3、线程解锁前,必须把工作内存中共享变量最新值立即刷新到主内存中
  • 24.2、JMM线程、主内存、工作内存关系示意图(JMM Thread,Main Memory,Work Memory Relation Diagram)

    其示意图,如下图所示

    JMM线程、主内存、工作内存关系示意图

  • 24.3、JMM8种内存交互操作(JMM Eight Memory Interactive Operations)

    • 内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
      • lock(锁定):作用于主内存的变量,把一个变量标识为线程独占状态
      • unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
      • read(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
      • load(载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
      • use(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
      • assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
      • store(存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
      • write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
    • JMM对这八种指令的使用,制定了如下规则
      • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
      • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
      • 不允许一个线程将没有assign的数据从工作内存同步回主内存
      • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
      • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
      • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
      • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
      • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

25、Volatile

Volatile是JVM(Java Virtual Machine,Java虚拟机)提供的轻量级同步机制

Volatile的3大特性:“1、不保证原子性(可以使用“Lock锁”、“Synchronized锁”、“原子类”来保证原子性);2、保证可见性(与“Lock锁”、“Synchronized锁”、“final关键字”一样来保证可见性);3、保证有序性(即,由于“内存屏蔽”,可以“禁止发生指令重排”的现象)”

  • 25.1、示例代码(Smaple Code)

其示例代码,如下所示

package com.xueshanxuehai.juc.jmm;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * Volatile:是JVM(Java Virtual Machine,Java虚拟机)提供的轻量级同步机制。
 * 3大特性:
 * 1、不保证原子性(可以使用“Lock锁”、“Synchronized锁”、“原子类”来保证原子性);
 * 2、保证可见性(与“Lock锁”、“Synchronized锁”、“final关键字”一样来保证可见性);
 * 3、保证有序性(即,由于“内存屏蔽”,可以“禁止发生指令重排”的现象)。
 */
public class Volatile {
    /**
     * 1、使用volatile关键字保证可见性
     * 2、使用原子类保证volatile原子性(原子类的底层都是直接和操作系统挂钩,在内存中修改值。)
     * 3、使用volatile关键字保证有序性
     */
    private volatile static int num=0;//初始化私有静态int变量
    private volatile static AtomicInteger atomicInteger= new AtomicInteger();//初始化私有静态AtomicInteger原子类对象
    public static void main(String[] args) {
        method1();//方法1
        method2();//方法2
    }
    public static void method1(){
        new Thread(()->{
            while(num==0){
                //加上关键字volatile后,才能保证可见性,从而才会退出循环,否则死循环
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num=1;
        System.out.println("num = " + num);
    }
    public static void add1(){
        num++;//不是一个原子性操作
    }
    public static void add2(){
        atomicInteger.getAndIncrement();//原子类操作(CAS底层原理)
    }
    public static void method2(){
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                for (int j = 1; j <= 100; j++) {
                    add1();//非原子性操作方法
                    add2();//原子性操作方法
                }
            }).start();
        }
        while(Thread.activeCount()>2){//当前活动线程(至少包含main线程、GC线程)
            Thread.yield();//礼让线程
        }
        System.out.println(Thread.currentThread().getName()+"线程,非原子性操作方法运算结果(错误):"+num+",原子性操作方法运算结果(正确):"+atomicInteger);
    }
}
  • 25.2、运行结果(Run Result)

    其运行结果,如下所示

num = 1
main线程,非原子性操作方法运算结果(错误)1001,原子性操作方法运算结果(正确)1000

26、指令重排(Instruction Rearrangement)

由于计算机并不一定按照程序代码顺序执行指令,所以会发生“指令重排”现象。如下所示

“指令重排”执行过程:“程序源代码->编译器优化重排->指令并行重排->内存系统重排->执行指令”。

计算机处理器发生“指令重排”时的依据是考虑“数据间的依赖性”。

比如:
	int a=0;//第1步
	int b=0;//第2步
	a = a+1;//第3步
	b = a+b;//第4步
其中,正常执行过程为“第1步、第2步、第3步、第4步”,其正确结果为“a=1,b=1”;
指令重排后可能出现的异常执行过程为“第1步、第2步、第4步、第3步”,其错误结果为“a=1,b=0”;

使用volatile可以避免发生“指令重排”现象。

27、单例模式(Singleton Mode)

一般可分为3类:“懒汉式单例、静态内部类单例、DCL懒汉式单例”

  • 27.1、懒汉式单例(Lazy Singleton)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.singletonmode;
    /**
     * 懒汉式单例
     */
    public class LazySingleton {
        //可能会浪费资源空间
        private byte[] byte1=new byte[1024*1024];
        private byte[] byte2=new byte[1024*1024];
        private byte[] byte3=new byte[1024*1024];
        private byte[] byte4=new byte[1024*1024];
        private LazySingleton() {
        }
        private final static LazySingleton LAZY_SINGLETON=new LazySingleton();
        public static LazySingleton getInstance(){
            return LAZY_SINGLETON;
        }
    }
    
  • 27.2、静态内部类单例(Static Inner Class Singleton)

    其示例代码,如下所示

    package com.xueshanxuehai.juc.singletonmode;
    /**
     * 静态内部类单例
     */
    public class StaticInnerClassSingleton {
        private StaticInnerClassSingleton() {
        }
        public static StaticInnerClassSingleton getInstance(){
            return InnerClass.STATIC_INNER_CLASS;
        }
        public static class InnerClass{
            private final static StaticInnerClassSingleton STATIC_INNER_CLASS=new StaticInnerClassSingleton();
        }
    }
    
  • 27.3、DCL懒汉式单例(Double Check Lock Lazy Singleton)

    DCL懒汉式单例,即,“双重检查锁懒汉式单例”

    • 27.3.1、示例代码(Smaple Code)

      package com.xueshanxuehai.juc.singletonmode;
      import java.lang.reflect.Constructor;
      import java.lang.reflect.Field;
      /**
       * DCL懒汉式单例:即,双重检查锁懒汉式单例(Double Check Lock Lazy Singleton)
       */
      public class DCLLazySingleton {
          private static boolean flag=false;
          private DCLLazySingleton() {
              synchronized (DCLLazySingleton.class){//类的同步锁
                  if(flag==false){
                      flag=true;
                  }else{
                      throw new RuntimeException("不要试图使用反射破坏单例");//自定义运行时异常
                  }
              }
          }
          private volatile static DCLLazySingleton dclLazySingleton;
          public static DCLLazySingleton getInstance(){
              //双重检查锁懒汉式单例
              if(dclLazySingleton==null){//第一重检查
                  synchronized (DCLLazySingleton.class){//类的同步锁
                      if(dclLazySingleton==null){//第二重检查
                          dclLazySingleton=new DCLLazySingleton();//不是一个原子操作
                      }
                  }
              }
              return dclLazySingleton;
          }
          public static void main(String[] args) {
              DCLLazySingleton instance1 = new DCLLazySingleton();
              System.out.println("instance1 = " + instance1);
              /**
               * 使用反射破坏单例
               */
              try {
                  Field flag = DCLLazySingleton.class.getDeclaredField("flag");//获取反射类属性
                  flag.setAccessible(true);//不强制执行Java语言访问检查
                  Constructor<DCLLazySingleton> declaredConstructor = DCLLazySingleton.class.getDeclaredConstructor(null);//获取反射类构造器
                  declaredConstructor.setAccessible(true);//不强制执行Java语言访问检查
                  flag.set(instance1,false);//设置反射类属性值
                  DCLLazySingleton instance2 = declaredConstructor.newInstance();//获取反射类实例
                  System.out.println("instance2 = " + instance2);
                  flag.set(instance2,false);//设置反射类属性值
                  DCLLazySingleton instance3 =declaredConstructor.newInstance();//获取反射类实例
                  System.out.println("instance3 = " + instance3);
              } catch (Exception e) {
                  e.printStackTrace();
              }
              /**
               * 使用反射不能破坏枚举单例
               */
              EnumSingleton enumSingleton1=EnumSingleton.INSTANCE;
              System.out.println("enumSingleton1 = " + enumSingleton1);
              try {
                  /**
                   * 获取反射枚举类构造器
                   * Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(null);
                   * 此写法会抛出异常:java.lang.NoSuchMethodException: com.xueshanxuehai.juc.singletonmode.EnumSingleton.<init>()
                   * 通过反编译工具(jad)得出正确写法为:Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
                   */
                  Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);//获取反射枚举类构造器
                  declaredConstructor.setAccessible(true);//不强制执行Java语言访问检查
                  EnumSingleton enumSingleton2 = declaredConstructor.newInstance();//获取反射类实例
                  System.out.println("enumSingleton2 = " + enumSingleton2);
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      //枚举类:枚举本身也是一个Class类,且默认不允许反射破坏单例
      enum EnumSingleton{
          INSTANCE;
          private EnumSingleton() {
          }
          public EnumSingleton getInstance(){
              return INSTANCE;
          }
      }
      
    • 27.3.2、运行结果(Run Result)

      instance1 = com.xueshanxuehai.juc.singletonmode.DCLLazySingleton@6d311334
      instance2 = com.xueshanxuehai.juc.singletonmode.DCLLazySingleton@448139f0
      instance3 = com.xueshanxuehai.juc.singletonmode.DCLLazySingleton@7cca494b
      enumSingleton1 = INSTANCE
      java.lang.IllegalArgumentException: Cannot reflectively create enum objects
      	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:492)
      	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
      	at com.xueshanxuehai.juc.singletonmode.DCLLazySingleton.main(DCLLazySingleton.java:68)
      

28、CAS(Compare And Swap)

CAS全称"Compare And Swap(比较与交换)",是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。java.util.concurrent包中的原子类就是通过CAS来实现了乐观锁

Java无法操作内存,但Java可以调用native关键字对应的C++方法,而C++可以操作内存,从而Java可以间接通过这种方式操作内存,效率很高

  • 28.1、CAS缺点(CAS Disadvantages)

    • 28.1.1、循环会耗时
    • 28.1.2、一次性只能保证一个共享变量的原子性
    • 28.1.3、ABA问题(狸猫换太子)
  • 28.2、示例代码(Smaple Code)

其示例代码,如下所示

package com.xueshanxuehai.juc.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * CAS(Compare And Swap),即,“比较并交换”
 * CAS是CPU的并发原语
 */
public class CAS {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2021);
        /**
         * public final boolean compareAndSet(int expectedValue, int newValue)
         * 若达到期望值,就更新;否则就不更新
         */
        System.out.println("非期望线程");
        System.out.println("--------------------------------------------------");
        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("--------------------------------------------------");
        System.out.println("期望线程");
        System.out.println("--------------------------------------------------");
        System.out.println(atomicInteger.compareAndSet(2021, 2023));
        System.out.println(atomicInteger.get());
        System.out.println("--------------------------------------------------");
    }
}
  • 28.3、运行结果(Run Result)

    其运行结果,如下所示

非期望线程
--------------------------------------------------
true
2022
true
2021
--------------------------------------------------
期望线程
--------------------------------------------------
true
2023
--------------------------------------------------

29、原子引用解决ABA问题(Atomic Reference To Solve ABA Problem)

原子引用就是“带版本号的原子操作”,对应原理为“乐观锁”,可以解决ABA问题

  • 29.1、示例代码(Smaple Code)

其示例代码,如下所示

package com.xueshanxuehai.juc.atomicreference;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
 * 原子引用解决ABA问题
 */
public class AtomicReference {
    /**
     * AtomicStampedReference原子类
     * 一般正常业务操作时,AtomicStampedReference<Integer>中泛型都是一个非包装类,且比较的都是非包装类对象
     * 特别注意:若其中泛型是一个包装类时,需注意其对象的引用问题(如Integer对象的缓存机制,默认范围“-128~127”)
     */
    private static AtomicStampedReference<Integer> atomicStampedReference=new AtomicStampedReference<>(1,1);
    public static void main(String[] args) {
        new Thread(()->{
            Integer reference = atomicStampedReference.getReference();//获取原子引用值
            int stamp = atomicStampedReference.getStamp();//获取原子引用版本号
            System.out.println(Thread.currentThread().getName()+"线程,原子引用值:"+reference+",原子引用版本号:" + stamp);
            try {
                TimeUnit.SECONDS.sleep(1);//延时1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(1,2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"线程,原子引用值:"+atomicStampedReference.getReference()+",原子引用版本号:" + atomicStampedReference.getStamp());
            atomicStampedReference.compareAndSet(2,1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"线程,原子引用值:"+atomicStampedReference.getReference()+",原子引用版本号:" + atomicStampedReference.getStamp());
        },"A").start();
        new Thread(()->{
            Integer reference = atomicStampedReference.getReference();//获取原子引用值
            int stamp = atomicStampedReference.getStamp();//获取原子引用版本号
            System.out.println(Thread.currentThread().getName()+"线程,原子引用值:"+reference+",原子引用版本号:" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);//延时2秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(1,3, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"线程,原子引用值:"+atomicStampedReference.getReference()+",原子引用版本号:" + atomicStampedReference.getStamp());
        },"B").start();
    }
}
  • 29.2、运行结果(Run Result)

    其运行结果,如下所示

A线程,原子引用值:1,原子引用版本号:1
B线程,原子引用值:1,原子引用版本号:1
A线程,原子引用值:2,原子引用版本号:2
A线程,原子引用值:1,原子引用版本号:3
B线程,原子引用值:3,原子引用版本号:4

30、可重入锁(Reentrant Lock)

可重入锁(Reentrant Lock)又名递归锁(Recursive Lock),是指同一个线程在外层方法获取锁时,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁

  • 30.1、示例代码(Smaple Code)

其示例代码,如下所示

package com.xueshanxuehai.juc.lock.reentrantlock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 可重入锁(Reentrant Lock),即,“递归锁(Recursive Lock)”
 */
public class ReentrantLockDemo {
    public static void main(String[] args) {
        MobilePhone1 mobilePhone1=new MobilePhone1();
        new Thread(()->{
            mobilePhone1.sendSMS();
        },"mp1_1").start();
        new Thread(()->{
            mobilePhone1.sendSMS();
        },"mp1_2").start();
        MobilePhone2 mobilePhone2=new MobilePhone2();
        new Thread(()->{
            mobilePhone2.sendSMS();
        },"mp2_1").start();
        new Thread(()->{
            mobilePhone2.sendSMS();
        },"mp2_2").start();
    }
}
//手机类1
class MobilePhone1{
    /**
     * synchronized锁
     */
    public synchronized void sendSMS(){
        System.out.println(this.getClass().getSimpleName()+"类,"+Thread.currentThread().getName()+"线程,发短信");
        callPhone();
        try {
            TimeUnit.SECONDS.sleep(1);//延时1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public synchronized void callPhone(){
        System.out.println(this.getClass().getSimpleName()+"类,"+Thread.currentThread().getName()+"线程,打电话");
    }
}
//手机类2
class MobilePhone2{
    /**
     * lock锁
     */
    private Lock lock= (Lock) new ReentrantLock();
    public void sendSMS(){
        lock.lock();//加锁
        try {
            System.out.println(this.getClass().getSimpleName()+"类,"+Thread.currentThread().getName()+"线程,发短信");
            callPhone();
            try {
                TimeUnit.SECONDS.sleep(1);//延时1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
    public void callPhone(){
        lock.lock();//加锁
        try {
            System.out.println(this.getClass().getSimpleName()+"类,"+Thread.currentThread().getName()+"线程,打电话");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//解锁
        }
    }
}
  • 30.2、运行结果(Run Result)

    其运行结果,如下所示

MobilePhone2,mp2_1线程,发短信
MobilePhone1,mp1_1线程,发短信
MobilePhone2,mp2_1线程,打电话
MobilePhone1,mp1_1线程,打电话
MobilePhone2,mp2_2线程,发短信
MobilePhone2,mp2_2线程,打电话
MobilePhone1,mp1_2线程,发短信
MobilePhone1,mp1_2线程,打电话

31、自旋锁(Spin Lock)

在许多场景中,同步资源的锁定时间很短,为了这一小段时间去切换线程,线程挂起和恢复现场的花费可能会让系统得不偿失。如果物理机器有多个处理器,能够让两个或以上的线程同时并行执行,我们就可以让后面那个请求锁的线程不放弃CPU的执行时间,看看持有锁的线程是否很快就会释放锁,而为了让当前线程“稍等一下”,我们需让当前线程进行自旋,如果在自旋完成后前面锁定同步资源的线程已经释放了锁,那么当前线程就可以不必阻塞而是直接获取同步资源,从而避免切换线程的开销。这就是自旋锁

  • 31.1、示例代码(Smaple Code)

SpinLockDemo类代码,如下所示

package com.xueshanxuehai.juc.lock.spinlock;
import java.util.concurrent.atomic.AtomicReference;
/**
 * 自旋锁(Spin Lock)
 * 底层使用CAS自旋锁
 */
public class SpinLockDemo {
    AtomicReference<Thread> atomicReference=new AtomicReference<>();//Thread类的原子引用
    public void lock(){//加锁
        System.out.println(this.getClass().getSimpleName()+"类,"+Thread.currentThread().getName()+"线程,lock");
        //自旋锁
        while(!atomicReference.compareAndSet(null,Thread.currentThread())){
        }
    }
    public void unLock(){//解锁
        System.out.println(this.getClass().getSimpleName()+"类,"+Thread.currentThread().getName()+"线程,unLock");
        atomicReference.compareAndSet(Thread.currentThread(),null);//CAS
    }
}

SpinLockTest类代码,如下所示

package com.xueshanxuehai.juc.lock.spinlock;
import java.util.concurrent.TimeUnit;
/**
 * 自旋锁(Spin Lock)测试
 */
public class SpinLockTest {
    public static void main(String[] args) {
        SpinLockDemo spinLock = new SpinLockDemo();//CAS自旋锁
        new Thread(()->{
            spinLock.lock();//加锁
            try {
                TimeUnit.SECONDS.sleep(3);//延时3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                spinLock.unLock();//解锁
            }
        },"t1").start();
        try {
            TimeUnit.SECONDS.sleep(1);//延时1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            spinLock.lock();//加锁
            try {
                TimeUnit.SECONDS.sleep(1);//延时1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                spinLock.unLock();//解锁
            }
        },"t2").start();
    }
}
  • 31.2、运行结果(Run Result)

    其运行结果,如下所示

SpinLockDemo,t1线程,lock
SpinLockDemo,t2线程,lock
SpinLockDemo,t1线程,unLock
SpinLockDemo,t2线程,unLock

32、死锁(Dead Lock)

多线程并发时,由于其中一个或多个线程一直在等待某个资源被释放,所以这些线程会一直阻塞,从而会发生“死锁”现象

  • 32.1、示例代码(Smaple Code)

其示例代码,如下所示

package com.xueshanxuehai.juc.lock.deadlock;
import java.util.concurrent.TimeUnit;
/**
 * 死锁(Dead Lock)
 * 查找死锁问题方式:
 * 方式1:查看异常Log档记录;
 * 方式2:查看异常堆栈信息;
 * 1、首先在终端“Terminal”窗口中输入“jps -l”,定位当前所有运行的java程序的进程号
 * 2、然后在终端“Terminal”窗口中输入“jstack java程序进程号”,查找java程序死锁问题异常堆栈信息
 */
public class DeadLockDemo {
    public static void main(String[] args) {
        String lock1="lock1";
        String lock2="lock2";
        new Thread(new DeadLockClass(lock1,lock2),"t1").start();
        new Thread(new DeadLockClass(lock2,lock1),"t2").start();
    }
}
class DeadLockClass implements Runnable{
    private String lock1;
    private String lock2;
    public DeadLockClass() {
    }
    public DeadLockClass(String lock1, String lock2) {
        this.lock1 = lock1;
        this.lock2 = lock2;
    }
    @Override
    public void run() {
        synchronized (lock1){
            System.out.println(this.getClass().getSimpleName()+"类,"+Thread.currentThread().getName()+"线程,"+lock1);
            try {
                TimeUnit.SECONDS.sleep(3);//延时3秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock2){
                System.out.println(this.getClass().getSimpleName()+"类,"+Thread.currentThread().getName()+"线程,"+lock2);
            }
        }
    }
}
  • 32.2、异常分析结果(Abnormal Analysis Result)

    其异常分析结果,如下所示

E:\Environment\Java\IDEA\StageOne>jps -l
5792 org.jetbrains.jps.cmdline.Launcher
6256 com.xueshanxuehai.juc.lock.deadlock.DeadLockDemo
2356
10344 jdk.jcmd/sun.tools.jps.Jps

E:\Environment\Java\IDEA\StageOne>jstack 6256
2021-09-11 10:54:32
Full thread dump Java HotSpot(TM) 64-Bit Server VM (16.0.1+9-24 mixed mode, sharing):

Threads class SMR info:
_java_thread_list=0x0000000027d3d930, length=15, elements={
0x00000000261a3260, 0x00000000261a3e30, 0x00000000261b1ae0, 0x00000000261b57f0,
0x00000000261b8100, 0x00000000261bbf10, 0x00000000261bcf50, 0x00000000261be130,
0x00000000261be690, 0x0000000019bfee80, 0x0000000027d292e0, 0x0000000027d29c80,
0x0000000027d509b0, 0x0000000027d50ed0, 0x000000000040ebb0
}

"Reference Handler" #2 daemon prio=10 os_prio=2 cpu=0.00ms elapsed=82.46s tid=0x00000000261a3260 nid=0x1e48 waiting on condition  [0x00000000271ef000]
   java.lang.Thread.State: RUNNABLE
        at java.lang.ref.Reference.waitForReferencePendingList(java.base@16.0.1/Native Method)
        at java.lang.ref.Reference.processPendingReferences(java.base@16.0.1/Reference.java:243)
        at java.lang.ref.Reference$ReferenceHandler.run(java.base@16.0.1/Reference.java:215)

"Finalizer" #3 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=82.46s tid=0x00000000261a3e30 nid=0x2b24 in Object.wait()  [0x000000002673f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(java.base@16.0.1/Native Method)
        - waiting on <0x0000000711c0d1b0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@16.0.1/ReferenceQueue.java:155)
        - locked <0x0000000711c0d1b0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@16.0.1/ReferenceQueue.java:176)
        at java.lang.ref.Finalizer$FinalizerThread.run(java.base@16.0.1/Finalizer.java:171)

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=82.45s tid=0x00000000261b1ae0 nid=0x1fb4 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 cpu=31.20ms elapsed=82.45s tid=0x00000000261b57f0 nid=0x1e54 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Service Thread" #6 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=82.45s tid=0x00000000261b8100 nid=0x27ac runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Deflation Thread" #7 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=82.45s tid=0x00000000261bbf10 nid=0x2adc runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #8 daemon prio=9 os_prio=2 cpu=46.80ms elapsed=82.45s tid=0x00000000261bcf50 nid=0x1488 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"C1 CompilerThread0" #11 daemon prio=9 os_prio=2 cpu=78.00ms elapsed=82.45s tid=0x00000000261be130 nid=0xe78 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

"Sweeper thread" #12 daemon prio=9 os_prio=2 cpu=0.00ms elapsed=82.45s tid=0x00000000261be690 nid=0x20d4 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Common-Cleaner" #13 daemon prio=8 os_prio=1 cpu=0.00ms elapsed=82.43s tid=0x0000000019bfee80 nid=0x15bc in Object.wait()  [0x000000002812e000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(java.base@16.0.1/Native Method)
        - waiting on <0x0000000711d1ce80> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@16.0.1/ReferenceQueue.java:155)
        - locked <0x0000000711d1ce80> (a java.lang.ref.ReferenceQueue$Lock)
        at jdk.internal.ref.CleanerImpl.run(java.base@16.0.1/CleanerImpl.java:140)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)
        at jdk.internal.misc.InnocuousThread.run(java.base@16.0.1/InnocuousThread.java:134)

"Monitor Ctrl-Break" #14 daemon prio=5 os_prio=0 cpu=15.60ms elapsed=82.40s tid=0x0000000027d292e0 nid=0xc4 runnable  [0x000000002878e000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.SocketDispatcher.read0(java.base@16.0.1/Native Method)
        at sun.nio.ch.SocketDispatcher.read(java.base@16.0.1/SocketDispatcher.java:46)
        at sun.nio.ch.NioSocketImpl.tryRead(java.base@16.0.1/NioSocketImpl.java:261)
        at sun.nio.ch.NioSocketImpl.implRead(java.base@16.0.1/NioSocketImpl.java:312)
        at sun.nio.ch.NioSocketImpl.read(java.base@16.0.1/NioSocketImpl.java:350)
        at sun.nio.ch.NioSocketImpl$1.read(java.base@16.0.1/NioSocketImpl.java:803)
        at java.net.Socket$SocketInputStream.read(java.base@16.0.1/Socket.java:976)
        at sun.nio.cs.StreamDecoder.readBytes(java.base@16.0.1/StreamDecoder.java:297)
        at sun.nio.cs.StreamDecoder.implRead(java.base@16.0.1/StreamDecoder.java:339)
        at sun.nio.cs.StreamDecoder.read(java.base@16.0.1/StreamDecoder.java:188)
        - locked <0x0000000711a92c38> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(java.base@16.0.1/InputStreamReader.java:178)
        at java.io.BufferedReader.fill(java.base@16.0.1/BufferedReader.java:161)
        at java.io.BufferedReader.readLine(java.base@16.0.1/BufferedReader.java:329)
        - locked <0x0000000711a92c38> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(java.base@16.0.1/BufferedReader.java:396)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:48)

"Notification Thread" #15 daemon prio=9 os_prio=0 cpu=0.00ms elapsed=82.40s tid=0x0000000027d29c80 nid=0x1bac runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"t1" #16 prio=5 os_prio=0 cpu=0.00ms elapsed=82.40s tid=0x0000000027d509b0 nid=0x1b94 waiting for monitor entry  [0x000000002838e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.xueshanxuehai.juc.lock.deadlock.DeadLockClass.run(DeadLockDemo.java:38)
        - waiting to lock <0x0000000711a57bc0> (a java.lang.String)
        - locked <0x0000000711a57b90> (a java.lang.String)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)

"t2" #17 prio=5 os_prio=0 cpu=15.60ms elapsed=82.40s tid=0x0000000027d50ed0 nid=0x1370 waiting for monitor entry  [0x000000002853f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.xueshanxuehai.juc.lock.deadlock.DeadLockClass.run(DeadLockDemo.java:38)
        - waiting to lock <0x0000000711a57b90> (a java.lang.String)
        - locked <0x0000000711a57bc0> (a java.lang.String)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)

"DestroyJavaVM" #18 prio=5 os_prio=0 cpu=78.00ms elapsed=82.40s tid=0x000000000040ebb0 nid=0x2900 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"VM Thread" os_prio=2 cpu=0.00ms elapsed=82.46s tid=0x00000000261a1820 nid=0x1dec runnable

"GC Thread#0" os_prio=2 cpu=0.00ms elapsed=82.47s tid=0x000000000043de30 nid=0x15b0 runnable

"G1 Main Marker" os_prio=2 cpu=0.00ms elapsed=82.47s tid=0x000000000043fb20 nid=0x1d2c runnable

"G1 Conc#0" os_prio=2 cpu=0.00ms elapsed=82.47s tid=0x0000000019b375f0 nid=0x1680 runnable

"G1 Refine#0" os_prio=2 cpu=0.00ms elapsed=82.47s tid=0x0000000019bf26c0 nid=0x1b20 runnable

"G1 Service" os_prio=2 cpu=0.00ms elapsed=82.47s tid=0x0000000026060080 nid=0x1638 runnable

"VM Periodic Task Thread" os_prio=2 cpu=0.00ms elapsed=82.40s tid=0x0000000027d4b800 nid=0x1ac8 waiting on condition

JNI global refs: 15, weak refs: 0


Found one Java-level deadlock:
=============================
"t1":
  waiting to lock monitor 0x0000000027d62450 (object 0x0000000711a57bc0, a java.lang.String),
  which is held by "t2"

"t2":
  waiting to lock monitor 0x0000000027d62530 (object 0x0000000711a57b90, a java.lang.String),
  which is held by "t1"

Java stack information for the threads listed above:
===================================================
"t1":
        at com.xueshanxuehai.juc.lock.deadlock.DeadLockClass.run(DeadLockDemo.java:38)
        - waiting to lock <0x0000000711a57bc0> (a java.lang.String)
        - locked <0x0000000711a57b90> (a java.lang.String)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)
"t2":
        at com.xueshanxuehai.juc.lock.deadlock.DeadLockClass.run(DeadLockDemo.java:38)
        - waiting to lock <0x0000000711a57b90> (a java.lang.String)
        - locked <0x0000000711a57bc0> (a java.lang.String)
        at java.lang.Thread.run(java.base@16.0.1/Thread.java:831)

Found 1 deadlock.
E:\Environment\Java\IDEA\StageOne>

参考资料(Reference Data):并发JMM概述java内存模型JMM理解整理java中的各种锁详细介绍

学习网站地址(即"学习网址",Learning Website Address):Java之JUC并发编程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值