Java并发编程面试这一篇就够了

JUC

1. 什么是JUC

学习:源码+官方文档

面试高频问!

在这里插入图片描述

java.util工具包

业务:普通的线程代码 Thread 很难实现

Runnable 没有返回值,效率相比Callable相对较低!

回顾之前:

在这里插入图片描述
在这里插入图片描述

2. 进程和线程

**进程:**程序的一次执行,QQ.exe,Music.exe 程序的集合一个进程往往可以包含多个线程,至少包含一个!Java默认有几个线程?2个main,GC

线程:开了一个进程Typora,写字,自动保存(线程负责)

对于Java而言:Thead,Runnable,Callable

Java真的能开启线程吗? 开不了(native方法,本地的C++方法)

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

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

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
        }
    }
}
//本地方法,底层的C++,Java无法直接操作硬件
private native void start0();

3. 并发和并行

并发编程:并发,并行

并发:多线程操作同一个资源

  • CPU一核,模拟出来多条线程,快速交替

并行:多个人一起走

  • CPU多核,多个线程可以同时执行 : 线程池

  • public static void main(String[] args) {
        //获取cpu核数
        //CPU 密集型,IO密集型
        int processors = Runtime.getRuntime().availableProcessors();//获取cpu的核数
        System.out.println(processors);
    }
    

并发编程的本质:充分利用CPU的资源

4. 多线程的回顾

4.1 线程的状态
public enum State {
       //新生
        NEW,

        //运行
        RUNNABLE,

        //阻塞
        BLOCKED,

        //等待,死死的等
        WAITING,

        //超时等待
        TIMED_WAITING,

        //终止
        TERMINATED;
    }
4.2 wait/sleap区别
  1. 来自不同的类
    • wait -> Object
    • sleep -> Thead
  2. 关于锁的释放
    • wait 会释放锁
    • sleep 抱着锁睡,不会释放
  3. 使用的范围
    • wait 必须在同步代码块中
    • sleep 可以在任何地方睡
  4. 是否需要捕获异常
    • wait 不需要捕获异常
    • sleep 需要捕获异常

5. Lock锁(重点)

传统的Synchronized锁

package edu.xalead.lock;
// 基本的买票例子
/**
 * 线程就是一个单独的资源类,没有任何的附属操作,拿来即用
 * 1. 属性,方法
 * 2. 降低耦合性
 */
public class SaleTicket {
    public static void main(String[] args) {
        //并发:多个线程操作同一个资源类,把资源类丢入线程
        Ticket ticket = new Ticket();//资源类
        //Runnable()函数式接口 jdk1.8 lambda表达式 (参数)->{代码}
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
//资源类 OOP
class Ticket{
    //属性,方法
    private int numberTicket = 50;
    //买票的方式
    //synchronized 本质是排队,锁
    public synchronized void sale(){
        if(numberTicket>0){
            System.out.println(Thread.currentThread().getName()+"卖出了第"+ (numberTicket--)+"票,还剩下"+numberTicket+"张票");
        }
    }
}

lock 接口(三个实现类)

在这里插入图片描述

在这里插入图片描述

//非公平锁:十分不公平,可以插队
public ReentrantLock() {
    sync = new NonfairSync();
}

//公平锁:严格先来后到
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

lock例子:

package edu.xalead.lock;

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

//买票例子
public class SaleTicket02 {
    public static void main(String[] args) {
        //并发:多个线程操作同一个资源类,把资源类丢入线程
        Ticket02 ticket = new Ticket02();//资源类
        //Runnable()函数式接口 jdk1.8 lambda表达式 (参数)->{代码}
        new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"B").start();
        new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"A").start();
        new Thread(()->{ for (int i = 0; i < 40; i++) ticket.sale(); },"C").start();
    }
}
//资源类 OOP

/**
 * lock 三部曲
 * 1. new ReentrantLock();
 * 2. lock.lock();
 * 3. finally -> lock.unlock();
 */
class Ticket02{
    //属性,方法
    private int numberTicket = 50;
    //买票的方式
    //lock 锁
    Lock lock = new ReentrantLock();

    public void sale(){
        lock.lock();//加锁
        try {
            //业务代码
            if(numberTicket>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第"+ (numberTicket--)+"票,还剩下"+numberTicket+"张票");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();//解锁
        }
    }
}

6. Synchronized和lock的区别

  1. Synchronized 内置的Java关键字,Lock是一个Java类
  2. Synchronized 无法判断获取锁的状态,Lock可以判断锁的状态
  3. Synchronized 会自动释放锁(异常了也会释放锁),Lock必须手动释放锁!如果不释放锁,死锁
  4. Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻等),Lock锁就不一定会等待下去lock.tryLock();
  5. Synchronized 可重入锁,不可以中断,非公平锁,Lock,可重入锁,可以判断锁,非公平(可以自自己设置 (true))
  6. Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码。

锁是什么,如何判断锁的是谁!

7. 生产者和消费者问题

7.1 Synchronized 锁实现

Synchronized 锁 解决pc问题

package edu.xalead.pc;

/**
 * 线程之间的通信问题:生产者和消费者问题!
 * 线程交替执行 A B线程操作同一个变量 num = 0
 * A num+1
 * B num-1
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
    }
}

//资源类,等待,业务,通知
class Data {
    private int number = 0;

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

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

在这里插入图片描述

7.2 虚假唤醒的问题

问题:如果四个线程呢,两加,两减,会有虚假唤醒的问题

在这里插入图片描述

在这里插入图片描述

因为是if判断,它只判断了一次,应该用while。

在这里插入图片描述

package edu.xalead.pc;

/**
 * 线程之间的通信问题:生产者和消费者问题!
 * 线程交替执行 A B线程操作同一个变量 num = 0
 * A num+1
 * B num-1
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

//资源类,等待,业务,通知
class Data {
    private int number = 0;

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

    //-1
    public synchronized void decrement() throws InterruptedException {
        while (number == 0) {
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "=>" + number);
        //通知其它线程,我-1完毕
        this.notifyAll();
    }
}
7.3 JUC版的生产者消费者

JUC版的生产者消费者问题

在这里插入图片描述

代码实现:

package edu.xalead.pc;

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

/**
 * 线程之间的通信问题:生产者和消费者问题!
 * 线程交替执行 A B线程操作同一个变量 num = 0
 * A num+1
 * B num-1
 */
public class B {
    public static void main(String[] args) {
        Data1 data = new Data1();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

//资源类.等待,业务,通知
class Data1 {
    private int number = 0;
    Lock lock = new ReentrantLock();//锁
    Condition condition = lock.newCondition();//同步监视器
    //+1
    public void increment() throws InterruptedException {
        
        lock.lock();
        try {
            while (number != 0) {
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //通知其它线程,我加1完毕
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

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

在这里插入图片描述

7.4 Condition优势

Condition优势:精准通知和唤醒线程以ABC的顺序执行

package edu.xalead.pc;

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

/**
 * 顺序执行,A->B->C
 */
public class C {
    public static void main(String[] args) {
        Data2 data2 = new Data2();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data2.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data2.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data2.printC();
            }
        },"C").start();
    }
}

class Data2{//资源类
    private Lock lock = new ReentrantLock();
    //同步监视器
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    private int number =1;//1 A执行 2 B执行 3 C
    public void printA(){
        lock.lock();
        try {
            //业务,判断,执行,通知
            while (number != 1){
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>A执行了");
            //唤醒指定的人
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
            while (number!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>B执行了");
            //通知C
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
            while (number!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>C执行了");
            //通知C
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

在这里插入图片描述

8. 8锁现象

如何判断锁的是谁!永远的知道什么是锁,锁的到底是谁!

锁的:new的对象,Class模板

深刻理解锁

  1. package edu.xalead.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 8锁,就是锁的8个问题
     * 1.标准情况下,两个线程先打印 谁呢,打电话,还是发短信? 1/发短信,2/打电话:锁的问题
     * 2.sendMessage延迟4秒,两个线程谁先打印呢?还是 1/发短信,2/打电话
     *
     */
    public class Test1 {
        public static void main(String[] args) {
            Phone phone = new Phone();
            new Thread(()->{
                phone.sendMessage();
            },"A").start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                phone.call();
            },"B").start();
        }
    }
    
    class Phone{
    
        //Synchronized 锁的是方法的调用者
        //两个方法用的同一个锁,谁先拿到谁执行
        public synchronized void sendMessage(){
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sendMessage");
        }
        public synchronized void call(){
            System.out.println("call");
        }
    }
    
    
  2. package edu.xalead.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 8锁,就是锁的8个问题
     * 1.标准情况下,两个线程先打印 谁呢,打电话,还是发短信?  1/发短信,2/打电话:锁的问题
     * 2.sendMessage延迟4秒,两个线程谁先打印呢?  还是 1/发短信,2/打电话
     * 3.增加了普通方法!先执行发短信,还是hello?  先打印hello,普通方法没有锁
     * 4.两个对象(资源),两个同步方法,先打印发短信,还是打电话? 先打电话
     */
    public class Test2 {
        public static void main(String[] args) {
            //两个调用者,就有两把锁(锁的是调用的对象)
            Phone1 phone1 = new Phone1();
            Phone1 phone2 = new Phone1();
            new Thread(()->{
                phone1.sendMessage();
            },"A").start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                phone2.call();
            },"B").start();
        }
    }
    
    class Phone1{
    
        //Synchronized 锁的是方法的调用者
        //两个方法用的同一个锁,谁先拿到谁执行
        public synchronized void sendMessage(){
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sendMessage");
        }
    
        public synchronized void call(){
            System.out.println("call");
        }
        //这里没有锁,不是同步方法不受锁的影响
        public void hello(){
            System.out.println("hello");
        }
    }
    
    
  3. package edu.xalead.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 8锁,就是锁的8个问题
     * 1.标准情况下,两个线程先打印 谁呢,打电话,还是发短信?  1/发短信,2/打电话:锁的问题
     * 2.sendMessage延迟4秒,两个线程谁先打印呢?  还是 1/发短信,2/打电话
     * 3.增加了普通方法!先执行发短信,还是hello?  先打印hello,而普通方法没有锁
     * 4.两个对象(资源),两个同步方法,先打印发短信,还是打电话? 先打电话
     * 5. 增加两个静态的同步方法,只有一个对象,先打印谁呢? 先发短信再打电话
     * 6. 增加两个静态的同步方法,两个对象!先打印谁呢? 与4不同锁的Class只有一个,先发短信再打电话
     */
    public class Test3 {
        public static void main(String[] args) {
            //两个对象只有一个类模板,锁的class
            Phone2 phone1 = new Phone2();
            Phone2 phone2 = new Phone2();
    
            new Thread(()->{
                phone1.sendMessage();
            },"A").start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                phone2.call();
            },"B").start();
        }
    }
    //只有一个唯一的class对象,全局唯一,所以锁只有一个
    class Phone2{
    
        //Synchronized 锁的是方法的调用者
        //static 静态方法
        //类加载就有了!锁的Class模板
        public static synchronized void sendMessage(){
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sendMessage");
        }
    
        public static synchronized void call(){
            System.out.println("call");
        }
    }
    
    
  4. package edu.xalead.lock8;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 8锁,就是锁的8个问题,有延迟的情况下
     * 1.标准情况下,两个线程先打印 谁呢,打电话,还是发短信?  1/发短信,2/打电话:锁的问题
     * 2.sendMessage延迟4秒,两个线程谁先打印呢?  还是 1/发短信,2/打电话:------只有一个锁,锁的调用者
     * 3.增加了普通方法!先执行发短信,还是hello?  先打印hello,普通方法没有锁
     * 4.两个对象(资源),两个同步方法,先打印发短信,还是打电话? 先打电话 ------锁的各自的调用者
     * 5.增加两个静态的同步方法,只有一个对象,先打印谁呢? 先发短信再打电话   ----- 锁的Class模板只有一个,谁先拿到锁,谁先
     * 6.增加两个静态的同步方法,两个对象!先打印谁呢? 与4不同锁的Class只有一个,先发短信再打电话
     * 7.一个静态同步,一个普通同步,一个对象,先打印谁呢?  先打电话再发短信
     * 8.一个静态同步,一个普通同步,两个对象,先打印谁呢?  先打电话再发短信  ------一个锁的调用者,一个锁的Class模板,两个锁,而一个有延迟
     */
    public class Test4 {
        public static void main(String[] args) {
    
            Phone3 phone1 = new Phone3();
            Phone3 phone2 = new Phone3();
    
            new Thread(()->{
                phone1.sendMessage();
            },"A").start();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()->{
                phone2.call();
            },"B").start();
        }
    }
    
    //只有一个唯一的class对象,全局唯一,所以锁只有一个
    class Phone3{
    
        //Synchronized 锁的是方法的调用者
        //static 静态方法
        //类加载就有了!锁的Class模板
    
        //静态同步方法,锁的类模板
        public static synchronized void sendMessage(){
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sendMessage");
        }
        //普通同步方法,锁的调用者
        public synchronized void call(){
            System.out.println("call");
        }
    }
    
    

小结

  1. new this, 具体的一个对象
  2. static Class 唯一的一个模板

9. 集合类不安全

9.1 List 不安全

List 不安全

package edu.xalead.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

// java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
    public static void main(String[] args) {
//        List<Integer> list = Arrays.asList(1, 2, 3);
//        list.forEach(System.out::println);
        //并发下ArrayList 不安全的
        /**
         * 解决方案:
         *  1. Vector 默认是安全的(不要回答这个)
         *  2. Collections.synchronizedList(new ArrayList<>());
         *  3. new CopyOnWriteArrayList<>(); 比 Vector(Syn) nb 在效率高(lock)
         */

        //CopyOnWrite 写入时复制,COW, 计算机程序设计领域的一种优化策略
        //多个线程调用的时候,List,读取的时候是固定的,写入时(覆盖)
        //在写入的时候避免覆盖,造成数据问题!
        //读写分离 MyCat(写入的时候复制一份,写完再复制回去)
//        List<String> list = new CopyOnWriteArrayList<>();
        List<String> list = Collections.synchronizedList(new ArrayList<>());
//        List<String> list = new Vector<>();
        for (int i = 1; i <= 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}
9.2 set 集合不安全

set 集合不安全

package edu.xalead.unsafe;

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

// java.util.ConcurrentModificationException 并发修改异常
// 解决方法:
// 1. Collections.synchronizedSet(new HashSet<>());
// 2. new CopyOnWriteArraySet<>(); 写入时复制
public class SetTest {
    public static void main(String[] args) {
        //HashSet底层是HashMap
        HashSet<String> set = new HashSet<>();
//        Set<String> set = Collections.synchronizedSet(new HashSet<>());
//        Set<String> set = new CopyOnWriteArraySet<>();

        for (int i = 1; i <= 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

HashSet底层:

public HashSet() {
    map = new HashMap<>();
}

add:本质map 的key 是无法重复的

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

private static final Object PRESENT = new Object();
9.3 Map不安全
package edu.xalead.unsafe;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
//java.util.ConcurrentModificationException
/**
 * 解决:
 *  1. Collections.synchronizedMap(new HashMap<>());
 *  2. new ConcurrentHashMap<>();
 */
public class MapTest {
    public static void main(String[] args) {

        /**
         *  问题:
         *  1. map是怎样用的? 工作中不用HashMap
         *     - 加载因子 loadFactor   默认0.75f,防止桶重复
         *     - 初始化容量 initialCapacity 默认16 1<<4
         *  2. 默认等价于什么
         */
//        HashMap<String, String> map = new HashMap<>();
//        Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

10. Callable

在这里插入图片描述

  1. 有返回值
  2. 可以抛出异常
  3. 方法不同,run()/call()

代码测试

一般启动类:new Thead(Runable).start(); 现在要启动Callable

在这里插入图片描述

需要适配器类实现中间过渡,Runable有个FutureTask的实现类

在这里插入图片描述

在这里插入图片描述

代码:

package edu.xalead.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //怎么启动Callable
        //适配下,Runable的一个实现类
        MyThead thead = new MyThead();
        FutureTask callable = new FutureTask(thead);

        new Thread(callable,"A").start();
        new Thread(callable,"B").start();//结果会被缓存,效率高

        String callableGet = (String) callable.get();//这个get 方法可能发生阻塞(耗时的方法)! 把他放到最后,或者通过异步通信处理
        System.out.println(callableGet);
    }
}

class MyThead implements Callable<String> {

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

细节:

  1. 有缓存
  2. 结果可能需要等待,会阻塞!

11. 常用辅助类(必会)

11.1 CountDownLatch

计数不重置

在这里插入图片描述

代码测试:

package edu.xalead.fuzhulei;

import java.util.concurrent.CountDownLatch;

//计数器(-)
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //倒计时,总数是6,必须要执行任务的时候再使用
        CountDownLatch count = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                count.countDown();// -1操作
                System.out.println(Thread.currentThread().getName()+"=> Go out!");
            },String.valueOf(i)).start();
        }
        //减完后等待归零,然后再向下执行
        count.await();
        System.out.println("Close Door");
    }
}

原理:

count.countDown();// -1操作

count.await(); //减完后等待归零,然后再向下执行

每次线程调用countDown数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行下面的代码

11.2 CyclicBarrier

在这里插入图片描述

加法计数,线程计数器,如果到不了指定的个数,就会阻塞

代码测试:

package edu.xalead.fuzhulei;

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

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        //收集七颗龙珠召唤神龙
        //主线程,召唤龙珠的线程
        //CyclicBarrier相当于屏障
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙成功");
        });
        for (int i = 1; i <= 7; i++) {
            final int temp = i;//提高了作用域,再lambda中拿到
            //lambda能操作到i吗
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"收集了"+temp+"第个龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
11.3 Semaphore(操作系统中信号量)

在这里插入图片描述

抢车位!

6车—3个停车位(123来了)456得等待

代码测试:

package edu.xalead.fuzhulei;

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

public class SemaphoreDemo {

    public static void main(String[] args) {
        //线程数量,停车位,资源有限,使得有序,限流!
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
               //acquire() 得到,获得
                try {
                    semaphore.acquire();//等待,拿到了
                    System.out.println(Thread.currentThread().getName()+"号抢到车位");
                    TimeUnit.SECONDS.sleep(2);//停五秒
                    System.out.println(Thread.currentThread().getName()+"号离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release(); //release() 释放
                }
            },String.valueOf(i)).start();
        }
    }
}

原理:

semaphore.acquire():获得,假设已经满了,等待,等待被释放!-1操作

semaphore.release();释放,会将当前的信号量释放,唤醒等待的线程。+1 操作

作用:多个线程共享资源互斥的访问,并发限流,控制最大的线程数

12. 读写锁ReadWriteLock

在这里插入图片描述

测试代码:

package edu.xalead.lock;

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

/**
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁) 多个线程可以同时占有
 * ReadWriteLock
 * 读-读 可以共存
 * 读-写 不能共存
 * 写-写 不能共存
 */
public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyCacheLock myCacheLock = new MyCacheLock();//资源缓存
        //5个线程写
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(()->{
                myCacheLock.put(temp+"",temp+"");
            }).start();
        }
        //5个线程读
        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(()->{
                myCacheLock.get(temp+"");
            }).start();
        }
    }
}

class MyCacheLock{
    private volatile HashMap<String,Object> map = new HashMap<>();//原子性操作

    //读写锁
    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()+"-写入->Ok");
        } 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);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"-读取->Ok");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

13. 阻塞队列

在这里插入图片描述

阻塞队列:

在这里插入图片描述

//BlockingQueue不是新东西
//什么情况下我们会使用阻塞队列:多线程并发处理,线程池!

在这里插入图片描述

学会使用队列:添加,移除

四组API:

方式抛出异常有返回值,不抛异常阻塞等待 死等超时等待
添加add()offer()put()offer(,)
移除remove()poll()tack()poll(,)
判断队列首element()peek()
package edu.xalead.blockq;

import java.util.concurrent.ArrayBlockingQueue;

public class TestBQ{
    public static void main(String[] args) {
        test1();
    }
    /**
     * 抛出异常
     */
    public static void test1(){
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        System.out.println("-----------------------------");
        //抛出异常 java.lang.IllegalStateException: Queue full
        // System.out.println(blockingQueue.add("d"));
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());

        // java.util.NoSuchElementException
        // System.out.println(blockingQueue.remove());
    }
}
/**
     * 有返回值,不抛出异常
     */
public static void test2(){
    //队列大小
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

    System.out.println(blockingQueue.offer("a"));
    System.out.println(blockingQueue.offer("b"));
    System.out.println(blockingQueue.offer("c"));
    System.out.println(blockingQueue.offer("d"));
    System.out.println("-----------------------------");
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
}

在这里插入图片描述

总的代码:

package edu.xalead.blockq;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class TestBQ{
    public static void main(String[] args) throws InterruptedException {
        //test1();
        //test2();
        //test3();
        test4();
    }
    /**
     * 抛出异常
     */
    public static void test1(){
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        System.out.println(blockingQueue.element());//查看队首元素
        System.out.println("-----------------------------");
        //抛出异常 java.lang.IllegalStateException: Queue full
        // System.out.println(blockingQueue.add("d"));
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());

        // java.util.NoSuchElementException
        // System.out.println(blockingQueue.remove());
    }

    /**
     * 有返回值,不抛出异常
     */
    public static void test2(){
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d"));
        System.out.println(blockingQueue.peek());//检查队首元素
        System.out.println("-----------------------------");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
    }

    /**
     * 等待,阻塞(一直阻塞)
     */
    public static void test3() throws InterruptedException {
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        //一直阻塞
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //blockingQueue.put("d");//队列没有位置了

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

    /**
     * 等待,阻塞(等待超时)
     */
    public static void test4() throws InterruptedException {
        //队列大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        //等待超过两秒就推出
        blockingQueue.offer("d",2, TimeUnit.SECONDS);
        System.out.println("-----------------------------");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));
    }
}

另一个实现类:SynchronousQueue 同步队列

没有容量1的容量,进去一个元素,必须等待取出来之后,才能往里放一个元素!put,tack操作

package edu.xalead.blockq;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class SynchronousQueueDemo {
    /**
     * 同步队列
     * 和其它BlockQueue不一样,SynchronousQueue不存储元素put了一个元素,必须从里面tack取出来,否则不能put进值!
     */
    public static void main(String[] args) {
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
        //放
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()+"->put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"->put 2");
                synchronousQueue.put("3");
                System.out.println(Thread.currentThread().getName()+"->put 3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"p").start();
        //取
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"tack->"+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"tack->"+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"tack->"+synchronousQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"q").start();
    }
}

14. 线程池(重点)

线程池必会:三大方法,7大参数,4种拒绝策略

池化技术

程序的运行,本质:占用系统资源!优化资源的使用!=>池化技术!

线程池,连接池,内存池,对象池

池化技术:事先准备好一些资源,有人要用,就来这里拿,用完之后还给我。

默认大小:2

max: cpu密集性,I/O密集性

线程池的好处: 线程复用,可以控制最大并发数,管理线程

  1. 降低资源消耗
  2. 提高响应的速度:不用新建和销毁
  3. 方便管理
14.1 三大方法

三大方法

package edu.xalead.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// Executors 工具类,有线程池3大方法

public class Demo01 {
    public static void main(String[] args) {
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定的线程池大小
        ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的

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

    }
}

ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程

在这里插入图片描述

ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定的线程池大小

在这里插入图片描述

ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的
在这里插入图片描述

14.2 七大参数

七大参数

源码分析

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

本质:ThreadPoolExecutor()源码,里面有七大参数

public ThreadPoolExecutor(
    int corePoolSize,//核心线程池大小
    int maximumPoolSize,//最大核心线程池大小
    long keepAliveTime,//超时没有人用就会释放
    TimeUnit unit,//超时单位
    BlockingQueue<Runnable> workQueue,//阻塞队列
    ThreadFactory threadFactory,//线程工厂,创建线程的,一般不用动
    RejectedExecutionHandler handler //拒绝策略
) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

阿里开发手册:

在这里插入图片描述

理解七大参数:

在这里插入图片描述

手动创建线程池:

package edu.xalead.pool;

import java.util.concurrent.*;

// Executors 工具类,原生实现线程池,自定义七大参数
public class Demo02 {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
//                new ThreadPoolExecutor.AbortPolicy()//银行满了,还有人来,不处理这个人的,抛出异常
//                new ThreadPoolExecutor.CallerRunsPolicy()//哪来的去哪里
//                new ThreadPoolExecutor.DiscardPolicy()//队列满了不会抛出异常,丢掉任务
                new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试和最早的竞争,失败就没了,不会抛弃异常
        );
        try{
            //最大承载:Queue + max
            //超出AbortPolicy()策略:java.util.concurrent.RejectedExecutionException:
            // Task edu.xalead.pool.Demo02$$Lambda$1/1096979270@7ba4f24f rejected from
            // java.util.concurrent.ThreadPoolExecutor@3b9a45b3[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8]
            for (int i = 1; i <= 9; i++) {
                //使用了线程池之后,用线程池创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"->ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }

    }
}

14.3 四种拒绝策略

在这里插入图片描述

  1. new ThreadPoolExecutor.AbortPolicy()//银行满了,还有人来,不处理这个人的,抛出异常
        
    //最大承载:Queue + max
    //超出AbortPolicy()策略:java.util.concurrent.RejectedExecutionException: Task edu.xalead.pool.Demo02$$Lambda$1/1096979270@7ba4f24f rejected fromjava.util.concurrent.ThreadPoolExecutor@3b9a45b3[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8]
    
  2. new ThreadPoolExecutor.CallerRunsPolicy()//哪来的去哪里
    

在这里插入图片描述

  1. new ThreadPoolExecutor.DiscardPolicy()//队列满了不会抛出异常,丢掉任务
    
  2. new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试和最早的竞争,失败就没了,不会抛弃异常
    
14.4 小结问题(*)

池的最大大小该如何去定义?

  1. cpu密集型:cpu可多核,并行执行,几核就是几,保证cpu效率最高,算Runtime.getRuntime().availableProcessors()电脑的核数
  2. I/O密集型:判断程序十分消耗io的线程 > 它 可2倍
    • 假设 程序 15 个大型任务,io十分占用资源!至少留下15个线程处理

15 四大函数式接口(必须掌握)

新时代程序员:lambda表达式,链式编程,函数式接口,Stream流式计算。

函数式接口: 只有一个方法的接口

Java 中很多,简化编程模型,在新版框架底层大量应用!

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
//foreach() 消费者类型的函数式接口
15.1. Function

Function 函数接口

在这里插入图片描述

package edu.xalead.function;

import java.util.function.Function;

/**
 * 函数型接口 函数式接口,有一个输入参数,有一个输出参数
 * 只要是,函数接口,可以用lambda表达式简化
 */
public class Demo01 {
    public static void main(String[] args) {
        // 工具类:输出输入的值
        //        Function<String, String> function = new Function<String, String>() {
        //            @Override
        //            public String apply(String o) {
        //                return o;
        //            }
        //        };

        Function<String, String> function = (str)->{return str;};
        System.out.println(function.apply("aaa"));
    }
}
15.2 predicate

断定型接口 predicate

在这里插入图片描述

package edu.xalead.function;

import java.util.function.Predicate;

/**
 * 断定型接口:有一个输入参数,返回值只能是 布尔值
 */
public class Demo02 {
    public static void main(String[] args) {
        //判断字符串是否为空
        //        Predicate<String> predicate = new Predicate<String>(){
        //
        //            @Override
        //            public boolean test(String str) {
        //                return str.isEmpty();
        //            }
        //        };
        Predicate<String> predicate = (str)->{return str.isEmpty();};

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

    }

}
15.3. Consumer

Consumer 消费型接口

在这里插入图片描述

package edu.xalead.function;

import java.util.function.Consumer;

/**
 * Consumer 消费型接口,只有输入没有返回值
 */
public class Demo03 {
    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        };
        Consumer<String> consumer = (str)->{
            System.out.println(str);
        };
        System.out.println("bbb");
    }
}

15.4 Supplier

在这里插入图片描述

package edu.xalead.function;

import java.util.function.Supplier;

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

        Supplier<Integer> supplier = ()->{return 1024;};
        System.out.println(supplier.get());
    }
}

16. Stream流式计算

什么是Stream流式计算

大数据:存储+计算

集合,MySQL本质是存储东西的;

计算都应该交给流来计算!

在这里插入图片描述

代码测试:

User:

package edu.xalead.stream;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private int age;
}

Test:

package edu.xalead.stream;

import java.util.Arrays;
import java.util.List;

/**
 * 题目要求:一分钟内完成此题,只能用一行代码实现!
 * 现在有5个用户!筛选:
 * 1. ID必须是偶数
 * 2. 年龄必须大于23岁
 * 3. 用户名转为大写字母
 * 4. 用户名字母倒着排序
 * 5. 只能输出一个用户
 */
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1,"a",21);
        User u2 = new User(2,"b",22);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5 = new User(6,"e",25);
        //集合就是存储
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
        //计算交给流
        //链式编程
        list.stream().
            filter(u -> {return u.getId()%2==0;})
            .filter(u ->{return u.getAge()>23;})//过滤,筛选
            .map(u->{return u.getName().toUpperCase();})//转换
            .sorted((u11,u22)->{return u22.compareTo(u11);})//排序
            .limit(1)//输出个数
            .forEach(System.out::println);
    }
}

17. ForkJoin

分支合并: 什么是ForkJoin

ForkJoin在jdk1.7出现,并行执行任务!提高效率。大数据量的情况下。

大数据:Map Reduce(把大任务拆分成小任务)

在这里插入图片描述

ForkJoin特点: 工作窃取,它维护的双端队列

在这里插入图片描述

ForkJoin的操作

如何使用ForkJoin

    1. ForkJoinPool 通过他来执行
    1. 计算任务 forkjoinPool.execute(ForkJoinTask task)
    1. 继承RecursiveTask 递归任务

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package edu.xalead.forkjoin;

/**
 * 求和计算的任务
 * 基本 -》 ForkJoin实现 -》Stream并行流实现
 */

import java.util.concurrent.RecursiveTask;

/**
 * 如何使用ForkJoin
 * 1. ForkJoinPool 通过他来执行
 * 2. 计算任务 forkjoinPool.execute(ForkJoinTask task)
 * 3. 继承RecursiveTask<Long> 递归任务
 */
public class ForkJoinDemo extends RecursiveTask<Long> {
    private long start;// 计算开始值
    private long end;//

    //临界值
    private Long temp =10000L;

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

    @Override
    protected Long compute() {
        if((end-start)<temp){
            long sum = 0L;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }else{
            //分支合并计算
            long mid = (start+end)/2;//中间值
            ForkJoinDemo task1 = new ForkJoinDemo(start, mid);
            task1.fork();//执行拆分,把任务压入线程队列
            ForkJoinDemo task2 = new ForkJoinDemo(mid+1, end);
            task2.fork();//执行拆分,把任务压入线程队列

            long sum = task1.join() + task2.join();
            return sum;
        }
    }
}

测试,包括流式计算:

package edu.xalead.forkjoin;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class TestForkJoin {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //        test1();//10380
        //        test2();//388
        test3();//237
    }

    //普通方法
    public static void test1() {
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (Long i = 0L; i <= 10_0000_0000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();

        System.out.println("sum="+ sum + "时间:" + (end - start));
    }
    //ForkJoin
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
        //        forkJoinPool.execute(task);//submit()异步提交
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();
        long end = System.currentTimeMillis();

        System.out.println("sum=" +sum+ "时间:" + (end - start));
    }
    //Stream 并行流计算,规约计算
    public static void test3() {
        long start = System.currentTimeMillis();
        long reduce = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0L, Long::sum);
        long end = System.currentTimeMillis();

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

}

18. 异步回调

Future 设计的初衷:对将来的某个时间进行建模

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package edu.xalead.future;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * 异步调用:CompletableFuture
 * //异步执行
 * //成功回调
 * //失败回调
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 发起一个请求
        // 没有返回值的runAsync 异步回调
//        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
//            try {
//                TimeUnit.SECONDS.sleep(2);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
//        });
//        System.out.println("111");
//        completableFuture.get();//获取阻塞执行执行结果

        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
            int i = 10 / 0;
            return 1024;
        });

        System.out.println(completableFuture.whenComplete((t, u) -> {
            System.out.println("t=>" + t);//正常的返回结果
            System.out.println("u=>" + u);//错误信息
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 233;//错误的返回结果
        }).get());
    }
}

19. JMM

请你谈谈对Volatile的理解

Volatile是Java虚拟机提供轻量级的同步机制

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

什么是JMM

Java内存模型,不存在的东西,概念,约定

关于JMM的一些同步的约定:

  1. 线程解锁前,独享的内存,独享的变量立即刷回主存
  2. 线程加锁前,必须读取主存中的最新值拷到自己工作内存
  3. 加锁和解锁是同一把锁

线程 工作内存 ,主内存

在这里插入图片描述

8种内存操作:(成对使用)

  1. read 读
  2. load 加载
  3. use 使用
  4. assign 赋值
  5. write 写入
  6. store 存储
  7. lock
  8. unlock

在这里插入图片描述

问题:程序不知道主内存的值已经被修改过了

在这里插入图片描述

package edu.xalead.Volatile;

import java.util.concurrent.TimeUnit;

public class JMMDemo {
    private static int num = 0;
    public static void main(String[] args) {//main线程
        new Thread(()->{
            while (num == 0){

            }
        }).start();

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

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

输出了1但程序并没有停止

在这里插入图片描述

20 .Volatile

解决上面的问题

可见性

package edu.xalead.Volatile;

import java.util.concurrent.TimeUnit;

public class JMMDemo {
    //不加volatile 程序就会死循环!
    //加volatile 可以保证可见性
    private volatile static int num = 0;
    public static void main(String[] args) {//main线程
        //这个线程感知不到内存的值发生了变化
        new Thread(()->{
            while (num == 0){

            }
        }).start();

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

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

不保证原子性

原子性:不可分割

比如:线程A在执行具体任务时,不能被打扰,也不能被分割。要么同时成功,要么同时失败。

在这里插入图片描述

package edu.xalead.jmmvolatile;

/**
 * 不保证原子性
 */
public class VolatileDemo {
    //volatile 不能保证原子性
    private volatile static int num = 0;

    public static void add() {
        num++; // 不是原子性操作
    }

    public static void main(String[] args) {
        //理论上结果应该为2万
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {//main GC线程 默认执行
            Thread.yield();//礼让
        }

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

如果不加lock和synchronized,怎么保证原子性?

反编译看底层:

在这里插入图片描述

使用原子类,解决原子性问题,不用lock

在这里插入图片描述

原子类为什么这么高级:

package edu.xalead.jmmvolatile;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 不保证原子性
 */
public class VolatileDemo {
    //volatile 不能保证原子性
    //原子类的Integer
    private volatile static AtomicInteger num = new AtomicInteger();

    public static void add() {
        //num++; // 不是原子性操作
        num.getAndIncrement();//CAS  +1操作,CPU并发
    }

    public static void main(String[] args) {
        //理论上结果应该为2万
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {//main GC线程 默认执行
            Thread.yield();//礼让
        }

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

这些类的底层直接和操作系统挂钩,直接操作内存中的值!Unsafe类是个很特殊的存在

指令重排

什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行

源代码–>编译器优化的重排–>指令并行也可能重排–>内存系统也可能重排–>执行

处理器在进行指令重排的时候,考虑:数据之间的依赖性!image-20200419210823968.png
)]

可能造成影响的结果:

在这里插入图片描述

volatile可以避免指令重排

内存屏障。cpu指令。作用:

  1. 保证特定操作的执行顺序!
  2. 可以保证某些变量的内存可见性!(利用这些特性volatile实现可见性)

指令重排问题,用的最多的在单例中

21. 玩转单例模式

1. 饿汉式单例
package edu.xalead.single;
//饿汉式单例
public class Hungry {
    //可能浪费空间
    private byte[] data1 = new byte[1*1024*1024];
    private byte[] data2 = new byte[1*1024*1024];
    private byte[] data3 = new byte[1*1024*1024];
    private byte[] data4 = new byte[1*1024*1024];
    //构造器私有
    private Hungry(){

    }
    //一上来就new一个
    private final static Hungry HUNGRY = new Hungry();

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

问题:一上来就加载,可能会浪费空间

2. 懒汉式单例

单线程下:

package edu.xalead.single;
//懒汉式单例
public class LazyMan {
    //构造器私有
    private LazyMan(){

    }

    private static LazyMan lazyMan;
    
    public static LazyMan getInstance(){
        if(lazyMan==null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}

DCL懒汉式单例:

package edu.xalead.single;
//懒汉式单例
public class LazyMan {
    //构造器私有
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"Ok");
    }

    private static LazyMan lazyMan;
    //双重检测锁模式 懒汉式单例 DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan = new LazyMan();
                }
            }
        }

        return lazyMan;
    }

    //多线程并发时
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                lazyMan.getInstance();
            }).start();
        }
    }
}

也可能出现问题:指令重排

package edu.xalead.single;
//懒汉式单例
public class LazyMan {
    //构造器私有
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"Ok");
    }

    private volatile static LazyMan lazyMan;
    //双重检测锁模式 懒汉式单例 DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){//在锁之前可能有已经有两个
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan = new LazyMan(); // 不是原子性操作
                    /**
                     * 经过步骤:
                     *  1. 分配内存空间
                     *  2. 执行构造方法,初始化对象
                     *  3. 把这个对象指向这个空间
                     *
                     *  123
                     *  132 A    //指令重排,先占用这个空间再初始化
                     *      B    //B线程 来的时候发现lazyMan不为空
                     */
                }
            }
        }

        return lazyMan; //
    }

    //多线程并发时
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                lazyMan.getInstance();
            }).start();
        }
    }
}

反射可能破坏单例(不安全的):道高一尺魔高一丈

package edu.xalead.single;

import java.lang.reflect.Constructor;

//懒汉式单例
public class LazyMan {
    private static boolean flag = false;//信号灯(可以是加密的东西)
    //构造器私有
    private LazyMan(){
        synchronized (LazyMan.class){
            if (!flag) {
                flag = true;
            } else {
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
            // if (lazyMan != null){
            //     throw new RuntimeException("不要试图使用反射破坏异常");
            // }
        }
    }

    private volatile static LazyMan lazyMan;
    //双重检测锁模式 懒汉式单例 DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){//在锁之前可能有已经有两个
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan = new LazyMan(); // 不是原子性操作
                    /**
                     * 经过步骤:
                     *  1. 分配内存空间
                     *  2. 执行构造方法,初始化对象
                     *  3. 把这个对象指向这个空间
                     *
                     *  123
                     *  132 A    //指令重排,先占用这个空间再初始化
                     *      B    //B线程 来的时候发现lazyMan不为空
                     */
                }
            }
        }

        return lazyMan; //
    }

    //反射
    public static void main(String[] args) throws Exception {
//        LazyMan lazyMan1 = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);//无视私有的默认构造器
        LazyMan lazyMan2 = declaredConstructor.newInstance();
        LazyMan lazyMan1 = declaredConstructor.newInstance();
        
        System.out.println(lazyMan1);
        System.out.println(lazyMan2);
    }
}

最终解决:枚举类型

在这里插入图片描述

package edu.xalead.single;

import java.lang.reflect.Constructor;

//enum 是一个什么? 本身也是一个Class类
public enum  EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }

}

class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle enumSingle = EnumSingle.INSTANCE;
        //        enumSingle.getInstance();
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle1 = declaredConstructor.newInstance();
        System.out.println(enumSingle);
        System.out.println(enumSingle1);
    }
}

在这里插入图片描述

enum没有无参构造。但idea,和反编译是有的。

在这里插入图片描述

22. 深入理解CAS

什么是CAS (特殊的Unsafe类):CAS 是CPU的并发原语

大厂你必须深入底层!(内功)

传统CAS:

package edu.xalead.cas;

import java.util.concurrent.atomic.AtomicInteger;

//传统CAS
public class CASDemo {
    //CAS : compareAndSet : 比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        /**
         * //源码: expect 期望,update 更新
         *  public final boolean compareAndSet(int expect, int update) {
         *       return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
         *  }
         */
        //如果我们期望的值达到了,那么就更新,否则就不更新,CAS 是CPU的并发原语
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        atomicInteger.getAndIncrement();//++操作
        //修改成功返回true
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yjKXL9AP-1587531618835)(C:\Users\艾健\Desktop\大厂面试\image-20200419224007908.png)]

底层:

atomicInteger.getAndIncrement();//++操作

在这里插入图片描述

–>unsafe

Unsafe:里面基本都为native方法

在这里插入图片描述

底层也是CAS:

在这里插入图片描述

面试:CAS底层是个自旋锁

在这里插入图片描述

结论:CAS:比较当前工作内存中的值和主内存的值,如果这个值是期望值,那么就执行此操作!如果不是就一直循环!

好处:自带原子性

缺点:

  1. 循环会耗时
  2. 一次性只能保证一个共享变量的原子性
  3. 会有,ABA的问题

CAS: ->ABA问题(狸猫换太子)

ABA问题:

在这里插入图片描述

对于我们平时写的SQl: 乐观锁来解决,判断锁有没有动过

我们期望捣乱的线程改了,要告诉我们。

23. 原子引用

原子引用: 带版本号的原子操作

解决ABA问题,引入原子引用!对应思想:乐观锁

坑:Integer使用了对象缓存机制,默认范围是-128~127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new 一定会创建新的对象分配新的内存空间;

在这里插入图片描述

package edu.xalead.cas;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
//原子引用
//AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
public class AtomicReferenceDemo {
    //CAS : compareAndSet : 比较并交换
    public static void main(String[] args) {
//        AtomicInteger atomicInteger = new AtomicInteger(2020);
        //在业务中引用的一般是对象
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference(1,1);//多了个版本号
        //与乐观锁原理相同
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println("a1=>"+stamp);

            atomicStampedReference.compareAndSet(1,2,stamp,stamp+1);
            System.out.println("a2=>"+atomicStampedReference.getStamp());

            atomicStampedReference.compareAndSet(2,1,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
            System.out.println("a3=>"+atomicStampedReference.getStamp());

        },"a").start();
        
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();
            System.out.println("b1=>"+stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(1,6,stamp,atomicStampedReference.getStamp()+1);
            System.out.println("b2=>"+atomicStampedReference.getStamp());
        }).start();
    }
}

24. 各种锁的理解

1. 公平锁、非公平锁

公平锁:非常公平,不能插队,必须先来后到的排队

非公平锁:非常公平,可以插队,3s ,3h(默认是非公平所)

lock:

public ReentrantLock() {
    sync = new NonfairSync();//默认非公平锁
}
//重载可以设置
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
2. 可重入锁

可重入锁(递归锁)
在这里插入图片描述

synchronized

package edu.xalead.lock;

public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sms();
        },"A").start();

        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}
//资源类
class Phone{

    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName()+"sms");
        call();//这里也有锁
    }

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

lock版

package edu.xalead.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//可重入锁,拿到外面的锁,也会拿到里面的锁
public class Demo02 {
    public static void main(String[] args) {
        Phone1 phone = new Phone1();
        new Thread(()->{
            phone.sms();
        },"A").start();
        
        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}

//资源类
class Phone1{

    Lock lock = new ReentrantLock();
    public void sms(){
        lock.lock();//细节问题,又两套锁
        //lock锁必须配对,否则就会死在里面
        try {
            System.out.println(Thread.currentThread().getName()+"sms");
            call();//这里也有锁,这是一套
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void call(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"call");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
3. 自旋锁

spinlock

在这里插入图片描述

自定义自旋锁

package edu.xalead.lock;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁
 */
public class SpinlockDemo {
    //int 0
    //Thread null
    AtomicReference<Thread> threadAtomicReference = new AtomicReference<>(null);
    // 加锁
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"==>mylock");

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

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

        threadAtomicReference.compareAndSet(thread,null);
    }
}

测试

package edu.xalead.lock;

import java.sql.Time;
import java.util.concurrent.TimeUnit;

public class TestSpinLock {
    public static void main(String[] args) {
        //底层CAS实现的自旋锁
        SpinlockDemo lock = new SpinlockDemo();

        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.myUnLock();
            }
        },"t1").start();

        new Thread(()->{
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.myUnLock();
            }
        },"t2").start();

    }

}

在这里插入图片描述

4. 死锁

一个线程所需的资源被另一个线程占有着,试图获取对方的锁。

在这里插入图片描述

死锁测试,怎么排查死锁!

测试:

package edu.xalead.lock;

import lombok.SneakyThrows;

import java.util.concurrent.TimeUnit;

public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        
        new Thread(new MyThread(lockA,lockB)).start();
        new Thread(new MyThread(lockB,lockA)).start();
    }
}

class MyThread implements Runnable{

    private String lockA;
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"->lock:"+lockA+"=>get"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"->lock:"+lockB+"=>get"+lockA);
            }
        }
    }
}

怎么排查死锁!

解决问题

  1. 使用jps-l查询进程号

在这里插入图片描述

  1. 使用jstack 进程号找到死锁问题(看堆栈信息)
    在这里插入图片描述

信息一般在最下面:

在这里插入图片描述

面试或者工作中!排除问题:

  1. 日志
  2. 看堆栈信息

解决死锁就是避免死锁的四个必要条件

欢迎访问我的个人博客:http://www.ayj.cn
学习视频:https://www.bilibili.com/video/BV1B7411L7tE?from=search&seid=6743650807996071874

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值