【1024面试必备】厂长爆肝万字之多线程高并发JUC编程⭐建议收藏

简介: 【1024面试必备】厂长爆肝万字之多线程高并发JUC编程⭐建议收藏

JUC学习

文章目录

1、什么是JUC

image.png

源码+官方文档

JUC是 java util concurrent

面试高频问JUC~!

image.png

java.util 是Java的一个工具包

业务:普通的线程代码 Thread

Runnable: 没有返回值、效率相比于Callable 相对较低!

image.png

2、线程和进程

进程:一个程序,允许一个java程序会进程里面会出现一个java.exe;数据+代码+pcb

一个进程可以包含多个线程,至少包含一个线程!

Java默认有几个线程?2个线程! main线程、GC线程

线程:开了一个进程qq,聊天打字,消息提示(线程负责的)

对于Java而言:Thread、Runable、Callable进行开启线程的。

**JAVA真的可以开启线程吗? 开不了的!**原因Java没有权限去开启线程、操作硬件的,这是一个native的一个本地方法,它调用的底层的C++代码。

并发、并行

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

  • CPU 只有一核,模拟出来多条线程,那么我们就可以使用CPU快速交替,来模拟多线程。

并行: 多个人并排行走。

  • CPU多核,多个线程可以同时执行。
public class Test {
    public static void main(String[] args) {
        //获取cpu的核数
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

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

线程的6个状态

public enum State {
      //创建
        NEW,
      //运行
        RUNNABLE,
      //阻塞
        BLOCKED,
      //等待
        WAITING,
      //超时等待
        TIMED_WAITING,
      //终止
        TERMINATED;
    }

面试题:谈一谈wait和sleep区别?

区别 wait sleep
操作的类 Object Thread
锁的释放 会释放锁 抱着锁睡觉
范围 同步代码块中 任何地方
异常捕获 不需要捕获异常 需要捕获异常

3、Lock锁(重点)

synchronized锁问题

package com.zmz.day01;/**
 * @ProjectName: Juc
 * @Package: com.zmz.day01
 * @ClassName: TicketTest
 * @Author: 张晟睿
 * @Date: 2021/9/5 14:01
 * @Version: 1.0
 */
//资源类 属性 + 方法 oop
class Ticket{
    private int num = 50;
    //卖票方式  synchronized 本质:队列 锁
    public synchronized void sale(){
        if(num > 0){
            System.out.println(Thread.currentThread().getName()+ " 卖出了第"+ num +" 张票,剩余:"+ --num +" 张票");
        }
    }
}
public class TicketTest {
    public static void main(String[] args) {
        //多线陈操作
        //并发:多个线程操作同一个资源ticket
        Ticket ticket = new Ticket();
        //@FunctionalInterface 函数式接口 jdk1.8之后 lambda表达式
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}

Lock接口

image.png

image.png

image.png

公平锁: 公平,必须先来后到~;

非公平锁: 不公平,可以插队;(默认为非公平锁)

使用Lock进行操作

package com.zmz.day01;/**
 * @ProjectName: Juc
 * @Package: com.zmz.day01
 * @ClassName: TicketTest2
 * @Author: 张晟睿
 * @Date: 2021/9/5 16:15
 * @Version: 1.0
 */
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 *@ClassName TicketTest2
 *@Description
 *@Author 张晟睿
 *@Date 2021/9/5
 **/
class Ticket2{
    /*
    * 加锁三步
    * 1.实例化lock对象
    * 2.lock加锁
    * 3.unlock解锁
    * */
    Lock l = new ReentrantLock();
    private int num = 50;
    //卖票方式  synchronized 本质:队列 锁
    public  void sale(){
        //加锁
        l.lock();
        try {
            //业务代码
            if(num > 0){
                System.out.println(Thread.currentThread().getName()+ " 卖出了第"+ num +" 张票,剩余:"+ --num +" 张票");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //解锁
            l.unlock();
        }
    }
}
public class TicketTest2 {
    public static void main(String[] args) {
        //多线陈操作
        //并发:多个线程操作同一个资源ticket
        Ticket ticket = new Ticket();
        //@FunctionalInterface 函数式接口 jdk1.8之后 lambda表达式
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 60; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}
区别 synchronized lock
名称 属于关键字 属于对象
状态 不可以获取锁的状态 可以获取锁的状态
锁的管理 自动释放锁 需要手动加锁以及释放锁
线程 自己抱着锁 等待
可重入锁,不可以中断的,非公平的 可重入的,可以判断锁,可以自己设置公平锁和非公平锁
代码同步 适合少量的代码同步 适合大量的代码同步

4、生产者消费者问题

synchronized版

package com.zmz.day01;/**
 * @ProjectName: Juc
 * @Package: com.zmz.day01
 * @ClassName: TicketTest3
 * @Author: 张晟睿
 * @Date: 2021/9/5 16:35
 * @Version: 1.0
 */
/**
 *@ClassName TicketTest3
 *@Description
 *@Author 张晟睿
 *@Date 2021/9/5
 **/
public class TicketTest3 {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
//判断等待  业务  唤醒
class Data{
    private int number = 0;
    //      +1操作
    public synchronized void increment() throws InterruptedException {
        if(number != 0 ){
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }
    //      -1操作
    public synchronized void decrement() throws InterruptedException{
        if (number == 0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }
}

问题存在,A线程B线程,现在如果我有四个线程A B C D!该怎么去解决问题

image.png

if判断改为While判断就可以解决虚假唤醒的问题。

package com.zmz.day01;/**
 * @ProjectName: Juc
 * @Package: com.zmz.day01
 * @ClassName: TicketTest3
 * @Author: 张晟睿
 * @Date: 2021/9/5 16:35
 * @Version: 1.0
 */
/**
 *@ClassName TicketTest3
 *@Description
 *@Author 张晟睿
 *@Date 2021/9/5
 **/
//线程之间的通讯问题:生产者和消费者的问题!  等待唤醒,通知唤醒
//线程交替执行 A   B操作同一个资源
public class TicketTest3 {
    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.decrement();
                } 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);
        this.notifyAll();
    }
    //      -1操作
    public synchronized void decrement() throws InterruptedException{
        while (number == 0){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        this.notifyAll();
    }
}

JUC版本的解决A B C D多线程的问题

image.png

package com.zmz.day01;/**
 * @ProjectName: Juc
 * @Package: com.zmz.day01
 * @ClassName: JucTest1
 * @Author: 张晟睿
 * @Date: 2021/9/5 19:34
 * @Version: 1.0
 */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 *@ClassName JucTest1
 *@Description
 *@Author 张晟睿
 *@Date 2021/9/5
 **/
public class JucTest1 {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(()->{for(int i=0;i<10;i++) {
            data.increment();
        }
        },"A").start();
        new Thread(()->{for(int i=0;i<10;i++) {
            data.decrement();
        }},"B").start();
        new Thread(()->{for(int i=0;i<10;i++) {
            data.increment();
        }
        },"C").start();
        new Thread(()->{for(int i=0;i<10;i++) {
            data.decrement();
        }
        },"D").start();
    }
}
class Data2{
    private int number = 0;
    //lock锁
    Lock l = new ReentrantLock();
    Condition condition = l.newCondition();
    public void increment() {
        l.lock();
        try {
            //业务
            while (number!=0){
                //等待操作
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程 我+1完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
    public void decrement()  {
        l.lock();
        try {
            //业务
            while (number==0){
                //等待操作
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知其他线程 我-1完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
}

Condition的优势:精准通知、唤醒的线程

package com.zmz.day01;/**
 * @ProjectName: Juc
 * @Package: com.zmz.day01
 * @ClassName: JucTest2
 * @Author: 张晟睿
 * @Date: 2021/9/5 19:52
 * @Version: 1.0
 */
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 *@ClassName JucTest2
 *@Description
 *@Author 张晟睿
 *@Date 2021/9/5
 **/
public class JucTest2 {
    public static void main(String[] args) {
        Data3 data3 = new Data3();
        new Thread(()->{
            for(int i=0;i<10;i++){
                data3.printA();
            }
        },"A").start();
        new Thread(()->{
            for(int i=0;i<10;i++){
                data3.printB();
            }
        },"B").start();
        new Thread(()->{
            for(int i=0;i<10;i++){
                data3.printC();
            }
        },"C").start();
    }
}
class Data3{
    private Lock l = new ReentrantLock();
    Condition condition1 = l.newCondition();
    Condition condition2 = l.newCondition();
    Condition condition3 = l.newCondition();
    private int flag = 1;
    public void printA(){
        l.lock();
        //判断 -> 执行 -> 通知
        try {
            while(flag != 1){
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "->A" );
            flag = 2;
            //唤醒指定线程
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
    public void printB(){
        l.lock();
        //判断 -> 执行 -> 通知
        try {
            while(flag != 2){
                //等待
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "->BB" );
            flag = 3;
            //唤醒指定线程
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
    public void printC(){
        l.lock();
        //判断 -> 执行 -> 通知
        try {
            while(flag != 3){
                //等待
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "->CCC" );
            flag = 1;
            //唤醒指定线程
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
}

5、8锁现象

1-2锁

package com.zmz.lock8;/**
 * @ProjectName: Juc
 * @Package: com.zmz.lock8
 * @ClassName: Test1
 * @Author: 张晟睿
 * @Date: 2021/9/5 21:18
 * @Version: 1.0
 */
import java.util.concurrent.TimeUnit;
/**
 *@ClassName Test1
 *@Description
 *@Author 张晟睿
 *@Date 2021/9/5
 **/
/*
* 8锁,就是关于锁的8个问题
* 1、标准情况下,两个线程先打印 发短信还是打电话?  发短信
* 2、sendSms方法延迟4s,两个线程先打印 发短信还是打电话?  发短信
* */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        //锁存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}
class Phone{
    //synchronized锁的对象是方法的调用者!
    //两个方法用的都是phone对象的锁!
    //谁先拿到谁执行!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }
    public synchronized void call(){
        System.out.println("打电话");
    }
}

image.png

3-4锁

package com.zmz.lock8;/** * @ProjectName: Juc * @Package: com.zmz.lock8 * @ClassName: Test2 * @Author: 张晟睿 * @Date: 2021/9/5 21:26 * @Version: 1.0 */import java.util.concurrent.TimeUnit;/** *@ClassName Test2 *@Description *@Author 张晟睿 *@Date 2021/9/5 **///3、增加一个普通方法后! 发短信还是Hello,发短信       hello//4、两个对象,两个同步方法,发短信还是打电话      打电话public class Test2 {    public static void main(String[] args) {        //两个对象,两把锁        Phone2 phone = new Phone2();        Phone2 phone2 = new Phone2();        //锁存在        new Thread(()->{            phone.sendSms();        },"A").start();        try {            TimeUnit.SECONDS.sleep(1);        } catch (InterruptedException e) {            e.printStackTrace();        }        new Thread(()->{            phone2.call();        },"B").start();    }}class Phone2{    //synchronized锁的对象是方法的调用者!    public synchronized void sendSms(){        try {            TimeUnit.SECONDS.sleep(4);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("发短信");    }    public synchronized void call(){        System.out.println("打电话");    }    //这里没有锁!bubu不是同步方法,不受锁的影响  public void hello(){        System.out.println("hello");    }}

image.png

image.png

5-6锁

package com.zmz.lock8;/** * @ProjectName: Juc * @Package: com.zmz.lock8 * @ClassName: Test3 * @Author: 张晟睿 * @Date: 2021/9/6 12:35 * @Version: 1.0 */import java.util.concurrent.TimeUnit;/** *@ClassName Test3 *@Description *@Author 张晟睿 *@Date 2021/9/6 **//** 5、增加两个静态同步方法,只有一个对象,先打印发短信还是打电话?  发短信* 6、两个对象!增加两个静态同步方法,只有一个对象,先打印发短信还是打电话  发短信* */public class Test3 {    public static void main(String[] args) {        //两个对象,两个调用者,两把锁!        //两个对象的class类模板只有一个,static,锁的是Class        Phone3 phone = new Phone3();        Phone3 phone2 = new Phone3();        //锁存在        new Thread(()->{            phone.sendSms();        },"A").start();        try {            TimeUnit.SECONDS.sleep(1);        } catch (InterruptedException e) {            e.printStackTrace();        }        new Thread(()->{            phone2.call();        },"B").start();    }}//Phone3唯一的一个Class对象class Phone3{    //synchronized锁的对象是方法的调用者!    //static 静态方法    //类一加载就有了!Class 模板 锁的是Class    public static synchronized void sendSms(){        try {            TimeUnit.SECONDS.sleep(4);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("发短信");    }    public static synchronized void call(){        System.out.println("打电话");    }}

image.png

7-8锁

package com.zmz.lock8;/** * @ProjectName: Juc * @Package: com.zmz.lock8 * @ClassName: Test4 * @Author: 张晟睿 * @Date: 2021/9/6 13:07 * @Version: 1.0 */import java.util.concurrent.TimeUnit;/** *@ClassName Test4 *@Description *@Author 张晟睿 *@Date 2021/9/6 **//** 1个静态同步方法,1个同步方法,1个对象 先打印发短信还是打电话?   打电话* 1个静态同步方法,1个同步方法,2个对象 先打印发短信还是打电话?   发短信* */public class Test4 {    public static void main(String[] args) {        //两个对象,两个调用者,两把锁!        //两个对象的class类模板只有一个,static,锁的是Class        Phone4 phone = new Phone4();        //锁存在        new Thread(()->{            phone.sendSms();        },"A").start();        try {            TimeUnit.SECONDS.sleep(1);        } catch (InterruptedException e) {            e.printStackTrace();        }        new Thread(()->{            phone.call();        },"B").start();    }}//Phone4唯一的一个Class对象class Phone4{    //synchronized锁的对象是方法的调用者!    //锁的是Class类模板    public static synchronized void sendSms(){        try {            TimeUnit.SECONDS.sleep(4);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("发短信");    }    //锁的是调用者    public synchronized void call(){        System.out.println("打电话");    }}

image.png

6、集合不安全

1、ArrayList集合不安全

package com;/** * @ProjectName: Juc * @Package: com * @ClassName: unsafe * @Author: 张晟睿 * @Date: 2021/9/6 19:37 * @Version: 1.0 */import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;/** *@ClassName unsafe *@Description *@Author 张晟睿 *@Date 2021/9/6 **/public class unsafe {    public static void main(String[] args) {        //高并发下的ArrayList真的安全么?        /**         * 解决方法         * 1、List<String> list = new Vector<String>();         * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());         * 3、List<String> list = new CopyOnWriteArrayList<>();         * */        List<String> list = new CopyOnWriteArrayList<>();        //启动10个多线程        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();        }    }}

CopyOnWriteArrayList源码分析

image.png

2、Set不安全

package com.zmz.unsafe;/** * @ProjectName: Juc * @Package: com.zmz.unsafe * @ClassName: SetSafe * @Author: 张晟睿 * @Date: 2021/9/6 21:20 * @Version: 1.0 */import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.UUID;import java.util.concurrent.CopyOnWriteArraySet;/** *@ClassName SetSafe *@Description *@Author 张晟睿 *@Date 2021/9/6 **/public class SetSafe {    public static void main(String[] args) {        //Set<String> set = new HashSet<>();        //Set<String> set = Collections.synchronizedSet(new HashSet<>());        Set<String> set = new CopyOnWriteArraySet<>();        for (int i = 1; i < 60; 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<>();    }  //HashSet本质就是Map集合  public boolean add(E e) {        return map.put(e, PRESENT)==null;    }private static final Object PRESENT = new Object();//不变的值

3、HashMap不安全

package com.zmz.unsafe;/** * @ProjectName: Juc * @Package: com.zmz.unsafe * @ClassName: MapSafe * @Author: 张晟睿 * @Date: 2021/9/6 21:27 * @Version: 1.0 */import java.util.Collections;import java.util.HashMap;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;/** *@ClassName MapSafe *@Description *@Author 张晟睿 *@Date 2021/9/6 **/public class MapSafe {    public static void main(String[] args) {        //Map<String,String> map = new HashMap<>();        //Map<String, Object> map = Collections.synchronizedMap(new HashMap<>());        Map<String, Object> 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();        }    }}

7、Callable

image.png

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

callable源码

image.png

image.png

image.png

image.png

代码测试

package com.zmz.callable;/** * @ProjectName: Juc * @Package: com.zmz.callable * @ClassName: CallableTest * @Author: 张晟睿 * @Date: 2021/10/3 16:52 * @Version: 1.0 */import com.sun.org.apache.bcel.internal.generic.NEW;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;/** *@ClassName CallableTest *@Description *@Author 张晟睿 *@Date 2021/10/3 **/public class CallableTest {    public static void main(String[] args) throws ExecutionException, InterruptedException {        // new Thread(new Runnable()).start();        // new Thread(new FutureTask<V>()).start();        // new Thread(new FutureTask<V>( Callable )).start();        new Thread().start();        MyThread myThread = new MyThread();        FutureTask futureTask = new FutureTask(myThread);//适配类        new Thread(futureTask,"A").start();        new Thread(futureTask,"B").start(); //结果存在缓存提交效率        Integer o = (Integer) futureTask.get();//获取返回的结果        //这个get方法可以会产生阻塞! 解决办法 放到最后一行或者异步通信        System.out.println(o);    }}class MyThread implements Callable<Integer>{    @Override    public Integer call() throws Exception {        System.out.println("call()方法");        //耗时操作        return 1024;    }}/*1、有缓存2、结果可能会等待,阻塞*/

8、常用的辅助类

1) CountDownLatch—减法计数器

package com.zmz.assist;/** * @ProjectName: Juc * @Package: com.zmz.assist * @ClassName: CountDownLatchDemo * @Author: 张晟睿 * @Date: 2021/10/3 18:00 * @Version: 1.0 */import java.util.concurrent.CountDownLatch;/** *@ClassName CountDownLatchDemo *@Description *@Author 张晟睿 *@Date 2021/10/3 **/public class CountDownLatchDemo {    public static void main(String[] args) throws InterruptedException {        CountDownLatch count = new CountDownLatch(10);        for (int i = 1; i <= 10; i++) {            new Thread(()->{                System.out.println(Thread.currentThread().getName() + "Go out");                count.countDown();//数量-1            },String.valueOf(i)).start();        }        count.await();//等计数器归零,然后再往下执行        System.out.println("Close Door");    }}

原理:

  • count.countDown();//数量-1
  • count.await();//等待计数器归零。然后再向下执行

每次有线程用countDown()数量-1,如果计算器变为0了,然后count.await()就被唤醒,继续下面的执行!

2) CyclicBarrier—加法计数器

package com.zmz.assist;/**
 * @ProjectName: Juc
 * @Package: com.zmz.assist
 * @ClassName: CycilcBarrierDemo
 * @Author: 张晟睿
 * @Date: 2021/10/3 18:23
 * @Version: 1.0
 */
import com.sun.org.apache.bcel.internal.generic.NEW;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
 *@ClassName CycilcBarrierDemo
 *@Description
 *@Author 张晟睿
 *@Date 2021/10/3
 **/
public class CycilcBarrierDemo {
    public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(7,()->{
            System.out.println("你已经凑齐了七颗龙珠!可以变身了");
        });
        for (int i = 1; i <= 7; i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+ "收集" +(temp) +"颗龙珠");
                try {
                    barrier.await();//等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

3) Semaphore

Semaphore:信号量,很多时候用来处理高并发。

image.png

package com.zmz.assist;/** * @ProjectName: Juc * @Package: com.zmz.assist * @ClassName: SemaphoreDemo * @Author: 张晟睿 * @Date: 2021/10/3 19:32 * @Version: 1.0 */import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;/** *@ClassName SemaphoreDemo *@Description *@Author 张晟睿 *@Date 2021/10/3 **/public class SemaphoreDemo {    public static void main(String[] args) {        Semaphore semaphore = new Semaphore(3);        for (int i = 1; i <= 6; i++) {            new Thread(()->{                try {                    semaphore.acquire();//获得                    System.out.println(Thread.currentThread().getName()+"得到车位");                    TimeUnit.SECONDS.sleep(2);                    System.out.println(Thread.currentThread().getName()+"离开车位");                } catch (InterruptedException e) {                    e.printStackTrace();                }   finally {                    semaphore.release();//释放                }            },String.valueOf(i)).start();        }    }}

原理:

  • acquire():获得,假设已经满了组需要等待,直到被释放为止
  • release():释放,会将当前的信号量释放+1,然后唤醒等待的线程!
    作用:多个共享的资源互斥使用!并发限流控制最大的线程数量!

9、读写锁ReadwriteLock

image.png

package com.zmz.lock;/** * @ProjectName: Juc * @Package: com.zmz.lock * @ClassName: ReadwritelockDemo * @Author: 张晟睿 * @Date: 2021/10/3 20:48 * @Version: 1.0 */import java.util.HashMap;import java.util.Map;/** *@ClassName ReadwritelockDemo *@Description *@Author 张晟睿 *@Date 2021/10/3 **/public class ReadwritelockDemo {    public static void main(String[] args) {        Mycache mycache = new Mycache();        //写入        for (int i = 1; i < 6; i++) {            final int temp = i;            new Thread(()->{                mycache.put(temp+"",temp+"");            },String.valueOf(i)).start();        }        //读取        for (int i = 1; i < 6; i++) {            final int temp = i;            new Thread(()->{                mycache.get(temp+"");            },String.valueOf(i)).start();        }    }}class Mycache{    private volatile Map<String , Object> map = new HashMap<>();    public void put(String key, Object value){        System.out.println(Thread.currentThread().getName() + "写入" + key);        map.put(key,value);        System.out.println(Thread.currentThread().getName() + "写入完成");    }    public void get(String key){        System.out.println(Thread.currentThread().getName() + "读取" + key);        Object o = map.get(key);        System.out.println(Thread.currentThread().getName() + "读取完成");    }}/*1写入15写入55写入完成4写入44写入完成3写入32写入21读取13写入完成1写入完成2读取22读取完成3读取33读取完成1读取完成2写入完成5读取55读取完成4读取44读取完成*/

我们可以看到出现了严重的插队问题!该如何去解决囊?我们使用读写锁来解决插队的问题。

修改后的操作

package com.zmz.lock;/**
 * @ProjectName: Juc
 * @Package: com.zmz.lock
 * @ClassName: ReadwritelockDemo
 * @Author: 张晟睿
 * @Date: 2021/10/3 20:48
 * @Version: 1.0
 */
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * @ClassName ReadwritelockDemo
 * @Description
 * @Author 张晟睿
 * @Date 2021/10/3
 **/
/*
*独占锁 (写锁)一次只能被一个线程占有
*共享锁 (读锁)多个线程可以同时占有
* ReadWriteLock
* 读-读   可以共存
* 读-写   不可以共存
* 写-写   不可以共存
* */
public class ReadwritelockDemo {
    public static void main(String[] args) {
        MycacheLock mycache = new MycacheLock();
        //写入
        for (int i = 1; i < 6; i++) {
            final int temp = i;
            new Thread(() -> {
                mycache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }
        //读取
        for (int i = 1; i < 6; i++) {
            final int temp = i;
            new Thread(() -> {
                mycache.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }
}
//加锁
class MycacheLock {
    private volatile Map<String, Object> map = new HashMap<>();
    //读写锁:更加细粒度的控制
    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() + "写入完成");
        } 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() + "读取完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}
class Mycache {
    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() + "写入完成");
    }
    public void get(String key) {
        System.out.println(Thread.currentThread().getName() + "读取" + key);
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName() + "读取完成");
    }
}
/*
1写入1
1写入完成
2写入2
2写入完成
3写入3
3写入完成
4写入4
4写入完成
5写入5
5写入完成
1读取1
1读取完成
5读取5
3读取3
3读取完成
4读取4
4读取完成
5读取完成
2读取2
2读取完成
*/

我们可以看到输出的结果在写入的时候有序的进行,读操作的时候可以无序的进行,可以看到已经到达我们预期的效果😊

10、堵塞队列

堵塞

image.png

image.png

image.png

image.png

什么情况下我们使用阻塞队列:多线程并发处理,线程池!

10.1 学会使用队列

添加、移除元素,现在有四组API

1) 四组API

方法 抛出异常 不会抛出异常,有返回值 阻塞等待 超时等待
添加 add() offer() put() offer(E e, long timeout, TimeUnit unit)
移除 remove() poll() take() poll(long timeout, TimeUnit unit)
判断首部 element() peek() - -
/*
* 抛出异常
* */
    public static void test1(){
        //队列大小3
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        //IllegalState ExceptionQueue full  抛出异常
        //System.out.println(blockingQueue.add("d"));
        //检测队首元素
        System.out.println(blockingQueue.element());                                                 System.out.println("================================================");
        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(){        //队列大小3        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);        System.out.println(blockingQueue.offer("a"));        System.out.println(blockingQueue.offer("b"));        System.out.println(blockingQueue.offer("c"));        //false        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());        //null        System.out.println(blockingQueue.poll());    }
/*     * 阻塞等待之☞死死的等待     * */    public static void test3() throws InterruptedException {        //队列大小3        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);        blockingQueue.put("a");        blockingQueue.put("b");        blockingQueue.put("c");        //blockingQueue.put("d");        //无检测队首元素        System.out.println("================================================");        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 {        //队列大小3        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());        blockingQueue.poll(2,TimeUnit.SECONDS);    }

2) SynchronizedQueue 同步队列

没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素

put take

package com.zmz.queue;/** * @ProjectName: Juc * @Package: com.zmz.queue * @ClassName: SyncQueue * @Author: 张晟睿 * @Date: 2021/10/8 14:18 * @Version: 1.0 */import java.util.concurrent.SynchronousQueue;import java.util.concurrent.TimeUnit;/** * 同步队列 * 和其他的lockQueue 不一样, SynchronousQueue 不存储元素 */public class SyncQueue {    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("2");                System.out.println(Thread.currentThread().getName() + "put 3");                synchronousQueue.put("3");            } catch (InterruptedException e) {                e.printStackTrace();            }        },"Thread1").start();        new Thread(()->{            try {                TimeUnit.SECONDS.sleep(3);                System.out.println(Thread.currentThread().getName() + "=>" + synchronousQueue.take());                TimeUnit.SECONDS.sleep(3);                System.out.println(Thread.currentThread().getName() + "=>" + synchronousQueue.take());                TimeUnit.SECONDS.sleep(3);                System.out.println(Thread.currentThread().getName() + "=>" + synchronousQueue.take());            } catch (InterruptedException e) {                e.printStackTrace();            } finally {            }        },"Thread2").start();    }}/*Thread1put 1Thread2=>1Thread1put 2Thread2=>2Thread1put 3Thread2=>3*/

11、线程池

线程池有三大方法,七大参数,四种拒绝策略

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

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

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

1) 线程池的好处:

  1. 降低资源消耗
  2. 提高响应速度
  3. 方便管理

线程复用,可以控制最大并发数,管理线程

2) 线程池: 三大方法

  • ExecutorService service = Executors.newSingleThreadExecutor();//单个线程
  • ExecutorService service = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
  • ExecutorService service = Executors.newCachedThreadPool();//可伸缩的,
package com.zmz.Pool;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * @ProjectName: Juc * @Package: com.zmz.ThreadPool * @ClassName: ThreadPool * @Author: 张晟睿 * @Date: 2021/10/8 16:44 * @Version: 1.0 */public class ThreadPool {    public static void main(String[] args) {        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程        ExecutorService threadPool2 = Executors.newFixedThreadPool(5); //创建一个固定的线程池的大小        ExecutorService threadPool3 = Executors.newCachedThreadPool(); //可伸缩的        //线程池用完必须要关闭线程池        try {            for (int i = 1; i <=100 ; i++) {                //通过线程池创建线程                threadPool3.execute(()->{                    System.out.println(Thread.currentThread().getName()+ " ok");                });            }        } catch (Exception e) {            e.printStackTrace();        } finally {            threadPool3.shutdown();        }    }}

3) 七大参数

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;}

image.png

image.png

package com.zmz.Pool;
import java.util.concurrent.*;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.Pool
 * @ClassName: ThreadPoolExecutorTest
 * @Author: 张晟睿
 * @Date: 2021/10/8 16:59
 * @Version: 1.0
 */
public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        // 获取cpu 的核数
        int max = Runtime.getRuntime().availableProcessors();
        ExecutorService service =new ThreadPoolExecutor(
                2,//核心线程池大小
                max,//最大的线程池大小
                3,//超时了没有人调用就会释放
                TimeUnit.SECONDS,//超时单位
                new LinkedBlockingDeque<>(3),//阻塞队列
                Executors.defaultThreadFactory(),//线程工厂 创建线程的
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略
        );
        try {
            for (int i = 1; i <= 5; i++) {
                service.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "运行成功");
                });
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            service.shutdown();
        }
    }
}

4) 拒绝策略

image.png

  • new ThreadPoolExecutor.AbortPolicy() 超出最大处理线程抛出异常
  • new ThreadPoolExecutor.CallerRunsPolicy() 从哪个线程创建就由那个线程执行
  • new ThreadPoolExecutor.DiscardPolicy() 队列满了不会抛出异常
  • new ThreadPoolExecutor.DiscardOldestPolicy() 尝试去和第一个竞争,也不会抛出异常

12、四大函数式接口

新时代的程序员👨‍💻:lambda表达式、链式编程、函数式接口、Stream流式计算

image.png

1) Function函数型接口

image.png

package com.zmz.FourFunction;import java.util.function.Function;/** * @ProjectName: Juc * @Package: com.zmz.FourFunction * @ClassName: functionDemo * @Author: 张晟睿 * @Date: 2021/10/8 17:15 * @Version: 1.0 */public class functionDemo {    public static void main(String[] args) {        Function<String, String> function = (str) -> {            return str;        };        System.out.println(function.apply("Hello,zmz!"));    }}

2) Predicate 断定型接口

image.png

package com.zmz.FourFunction;import java.util.function.Predicate;/** * @ProjectName: Juc * @Package: com.zmz.FourFunction * @ClassName: PredicateDemo * @Author: 张晟睿 * @Date: 2021/10/8 17:18 * @Version: 1.0 */public class PredicateDemo {    public static void main(String[] args) {        Predicate<String> predicate = (str) -> {return str.isEmpty();};        // false        System.out.println(predicate.test("zmz"));        // true        System.out.println(predicate.test(""));    }}

3) Suppier 供给型接口

image.png

package com.zmz.FourFunction;import java.util.function.Supplier;/** * @ProjectName: Juc * @Package: com.zmz.FourFunction * @ClassName: SuppierDemo * @Author: 张晟睿 * @Date: 2021/10/8 17:21 * @Version: 1.0 */public class SuppierDemo {    public static void main(String[] args) {        Supplier<String> supplier = ()->{return "1024";};        System.out.println(supplier.get());    }}

4) Consummer 消费型接口

image.png

package com.zmz.FourFunction;import java.util.function.Consumer;/** * @ProjectName: Juc * @Package: com.zmz.FourFunction * @ClassName: ConsummerDemo * @Author: 张晟睿 * @Date: 2021/10/8 17:21 * @Version: 1.0 */public class ConsummerDemo {    public static void main(String[] args) {        Consumer<String> consumer = (str)->{            System.out.println(str);        };        consumer.accept("zmz");    }}

13、Stream 流式计算

package com.zmz.Stream;/** * @ProjectName: Juc * @Package: com.zmz.Stream * @ClassName: User * @Author: 张晟睿 * @Date: 2021/10/8 18:01 * @Version: 1.0 */public class User {    private int id;    private String name;    private int age;    public User(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;    }}
package com.zmz.Stream;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.Stream
 * @ClassName: StreamDemo
 * @Author: 张晟睿
 * @Date: 2021/10/8 18:00
 * @Version: 1.0
 *
 * * 题目要求: 用一行代码实现
 *  * 1. Id 必须是偶数
 *  * 2.年龄必须大于23
 *  * 3. 用户名转为大写
 *  * 4. 用户名倒序
 *  * 5. 只能输出一个用户
 */
import java.util.Arrays;
import java.util.List;
public class StreamDemo {
    public static void main(String[] args) {
        User u1 = new User(1, "a", 23);
        User u2 = new User(2, "b", 23);
        User u3 = new User(3, "c", 23);
        User u4 = new User(6, "d", 24);
        User u5 = new User(4, "e", 25);
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
        // lambda、链式编程、函数式接口、流式计算
        list.stream()
                .filter(user -> {return user.getId()%2 == 0;})
                .filter(user -> {return user.getAge() > 20;})
                .map(user -> {return user.getName().toUpperCase();})
                .sorted((user1, user2) -> {return user2.compareTo(user1);})
                .limit(1)
                .forEach(System.out::println);
    }
}

14、ForkJoin—多线并发处理框架

什么是ForkJoin?

ava.util.concurrent.ForkJoinPool由Java大师Doug  Lea主持编写,它可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。本文中对Fork/Join框架的讲解,基于JDK1.8+中的Fork/Join框架实现,参考的Fork/Join框架主要源代码也基于JDK1.8+。

这几篇文章将试图解释Fork/Join框架的知识点,以便对自己、对各位读者在并发程序的设计思路上进行一些启发。文章将首先讲解Fork/Join框架的基本使用,以及其中需要注意的使用要点;接着使用Fork/Join框架解决一些实际问题;最后再讲解Fork/Join框架的工作原理。

image.png

1)ForkJoin 特点: 工作窃取!

image.png

2)如果使用ForkJoin

第一步,通过ForkJoinPool来执行

第二步,计算任务 execute(ForkJoinTask<?> task)

第三步,计算类要去继承ForkJoinTask

ForkJoin 的计算类

ForkJoinComputer.java

package com.zmz.ForkJoin;
import java.util.concurrent.RecursiveTask;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.ForkJoin
 * @ClassName: ForkJoinComputer
 * @Author: 张晟睿
 * @Date: 2021/10/9 15:17
 * @Version: 1.0
 */
public class ForkJoinComputer extends RecursiveTask<Long> {
    private long start;
    private long end;
    /** 临界值 */
    private long temp = 1000000L;
    public ForkJoinComputer(long start, long end) {
        this.start = start;
        this.end = end;
    }
    /**
     * 计算方法
     * @return
     */
    @Override
    protected Long compute() {
        if ((end - start) < temp) {
            Long sum = 0L;
            for (Long i = start; i < end; i++) {
                sum += i;
            }
            return sum;
        }else {
            // 使用ForkJoin 分而治之 计算
            //1 . 计算平均值
            long middle = (start + end) / 2;
            ForkJoinComputer forkJoinDemo1 = new ForkJoinComputer(start, middle);
            // 拆分任务,把线程压入线程队列
            forkJoinDemo1.fork();
            ForkJoinComputer forkJoinDemo2 = new ForkJoinComputer(middle, end);
            forkJoinDemo2.fork();
            long taskSum = forkJoinDemo1.join() + forkJoinDemo2.join();
            return taskSum;
        }
    }
}

测试类 ForkJoinTest.java

package com.zmz.ForkJoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.ForkJoin
 * @ClassName: ForkJoinTest
 * @Author: 张晟睿
 * @Date: 2021/10/9 15:18
 * @Version: 1.0
 */
public class ForkJoinTest {
    private static final long SUM = 20_0000_0000;
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();
        test2();
        test3();
    }
    /**
     * 使用普通方法
     */
    public static void test1() {
        long star = System.currentTimeMillis();
        long sum = 0L;
        for (long i = 1; i < SUM ; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(sum);
        System.out.println("普通程序猿——时间:" + (end - star));
        System.out.println("============================");
    }
    /**
     * 使用ForkJoin 方法
     */
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinComputer(0L, SUM);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long along = submit.get();
        System.out.println(along);
        long end = System.currentTimeMillis();
        System.out.println("中级程序猿——时间:" + (end - start));
        System.out.println("--------------");
    }
    /**
     * 使用 Stream 流计算
     */
    public static void test3() {
        long start = System.currentTimeMillis();
        long sum = LongStream.range(0L, 20_0000_0000L).parallel().reduce(0, Long::sum);
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("高级程序猿——时间:" + (end - start));
        System.out.println("--------------");
        System.out.println("============================");
    }
}

image.png

分析一下高级程序猿的处理:

image.png

.parallel().reduce(0, Long::sum)使用一个并行流去计算整个计算,提高效率。

15、异步回调

Future 设计的初衷:对将来的某个事件结果进行建模!

其实就是前端 —》发送ajax异步请求给后端

image.png

但是我们平时都使用CompletableFuture

(1)没有返回值的runAsync异步回调

package com.zmz.Async;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;/** * @ProjectName: Juc * @Package: com.zmz.Async * @ClassName: runAsync * @Author: 张晟睿 * @Date: 2021/10/11 18:58 * @Version: 1.0 */public class runAsync {    public static void main(String[] args) throws ExecutionException, InterruptedException    {        // 发起 一个 请求        System.out.println(System.currentTimeMillis());        System.out.println("---------------------");        CompletableFuture<Void> future = CompletableFuture.runAsync(()->{            //发起一个异步任务            try {                TimeUnit.SECONDS.sleep(2);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(Thread.currentThread().getName()+".....");        });        System.out.println(System.currentTimeMillis());        System.out.println("------------------------------");        //输出执行结果        System.out.println(future.get());  //获取执行结果    }}

(2)有返回值的异步回调supplyAsync

package com.zmz.Async;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;/** * @ProjectName: Juc * @Package: com.zmz.Async * @ClassName: supplyAsync * @Author: 张晟睿 * @Date: 2021/10/11 19:09 * @Version: 1.0 */public class supplyAsync {    public static void main(String[] args) throws ExecutionException, InterruptedException {        //有返回值的异步回调        CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{            System.out.println(Thread.currentThread().getName());            try {                TimeUnit.SECONDS.sleep(2);                int i=1/0;            } catch (InterruptedException e) {                e.printStackTrace();            }            return 1024;        });        System.out.println(completableFuture.whenComplete((t, u) -> {            /*我们可以看到whenComplete以上的程序有两个参数,一个是t 一个是u            T:是代表的 正常返回的结果;            U:是代表的 抛出异常的错误信息;           如果发生了异常,get可以获取到exceptionally返回的值;            */            //success 回调            System.out.println("t=>" + t); //正常的返回结果            System.out.println("u=>" + u); //抛出异常的 错误信息        }).exceptionally((e) -> {            //error回调            System.out.println(e.getMessage());            return 404;        }).get());    }}

16、JMM(Java Memory Model )

1)我们先了解一下什么JMM?

JMM:JAVA内存模型,不存在的东西,抽象的,是一个概念,也是一个约定!

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

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

线程中分为 工作内存、主内存

八种操作:

名称 描述
Read(读取) 作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
load(载入) 作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
Use(使用) 作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
assign(赋值) 作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
store(存储) 作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
write(写入) 作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
lock(锁定) 作用于主内存的变量,把一个变量标识为线程独占状态
unlock(解锁) 作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

image.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yTIjbiQ8-1635035368123)(https://gitee.com/z6135/cloudimage/raw/master/img/juc1 (2)].png)

对于八种操作给了相应的规定:

  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
  • 不允许一个线程将没有assign的数据从工作内存同步回主内存
  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施use、store操作之前,必须经过assign和load操作
  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

image.png

遇到问题:程序不知道主存中的值已经被修改过了!

17、volatile

1)保证可见性

package com.zmz.JMM;import java.util.concurrent.TimeUnit;/** * @ProjectName: Juc * @Package: com.zmz.JMM * @ClassName: JMMdemo01 * @Author: 张晟睿 * @Date: 2021/10/11 20:43 * @Version: 1.0 */public class JMMdemo01 {    // 如果不加volatile 程序会死循环    // 加了volatile是可以保证可见性的    private volatile static Integer number = 0;    public static void main(String[] args) {        //main线程        //子线程1        new Thread(()->{            while (number==0){            }        }).start();        try {            TimeUnit.SECONDS.sleep(2);        } catch (InterruptedException e) {            e.printStackTrace();        }        //子线程2        new Thread(()->{            while (number==0){            }        }).start();        try {            TimeUnit.SECONDS.sleep(2);        } catch (InterruptedException e) {            e.printStackTrace();        }        number=1;        System.out.println(number);    }}

2)不保证原子性

原子性:意思就是说不可分割,举一个例子就是说线程A在执行任务的时候,不能被打扰的,也不能被分割的,要么同时成功,要么同时失败。

package com.zmz.JMM;/** * @ProjectName: Juc * @Package: com.zmz.JMM * @ClassName: JMMdemo02 * @Author: 张晟睿 * @Date: 2021/10/11 20:46 * @Version: 1.0 */public class JMMdemo02 {    private static volatile int num = 0;    public static void add(){        num++;        //++ 不是一个原子性操作,是2个~3个操作    }    public static void main(String[] args) {        //理论上number  === 20000        for (int i = 1; i <= 20; i++) {            new Thread(()->{                for (int j = 1; j <= 1000 ; j++) {                    add();                }            }).start();        }        while (Thread.activeCount()>2){            //main  gc            Thread.yield();        }        System.out.println(Thread.currentThread().getName()+",num="+num);    }}

使用原子类

image.png

package com.zmz.JMM;import java.util.concurrent.atomic.AtomicInteger;/** * @ProjectName: Juc * @Package: com.zmz.JMM * @ClassName: JMMdemo03 * @Author: 张晟睿 * @Date: 2021/10/11 21:05 * @Version: 1.0 */public class JMMdemo03 {    private static volatile AtomicInteger number = new AtomicInteger();    public static void add(){//        number++;        number.incrementAndGet();  //底层是CAS保证的原子性    }    public static void main(String[] args) {        //理论上number  === 20000        for (int i = 1; i <= 20; i++) {            new Thread(()->{                for (int j = 1; j <= 1000 ; j++) {                    add();                }            }).start();        }        while (Thread.activeCount()>2){            //main  gc            Thread.yield();        }        System.out.println(Thread.currentThread().getName()+",num="+number);    }}

这些类的底层都直接和操作系统挂钩!是在内存中修改值。

3)禁止指令重排

什么是指令重排?

我们写的程序,计算机并不是按照我们自己写的那样去执行的

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

处理器在进行指令重排的时候,会考虑数据之间的依赖性!

int x=2; //1
int y=4; //2
x=x+10;   //3
y=x*x;   //4
//我们期望的执行顺序是 1_2_3_4  可能执行的顺序会变成3124 1423
//可不可能是 4123? 不可能的
1234567

可能造成的影响结果:前提:a b x y这四个值 默认都是0

线程A 线程B
x=a y=b
b=1 a=2

正常的结果: x = 0; y =0

线程A 线程B
b=1 a=2
x=a y=b

可能在线程A中会出现,先执行b=1,然后再执行x=a

在B线程中可能会出现,先执行a=2,然后执行y=b

那么就有可能结果如下:x=4; y=2

volatile可以避免指令重排:

volatile中会加一道内存的屏障,这个内存屏障可以保证在这个屏障中的指令顺序。

内存屏障:CPU指令。作用:保证特定的操作的执行顺序;可以保证某些变量的内存可见性(利用这些特性,就可以保证volatile实现的可见性)

image.png

4)总结

  • 由于内存屏障,可以保证避免指令重排的现象产生
  • 不能保证原子性
  • volatile可以保证可见性

🥰面试题:在哪里用这个内存屏障用得最多呢?

单例模式

18、单例模式

1)饿汉式

package single;
//饿汉式单例模式
@SuppressWarnings("all")
public class Hungry {
  private byte[] date1= new byte[1024*1024];
  private byte[] date2= new byte[1024*1024];
  private byte[] date3= new byte[1024*1024];
  private byte[] date4= new byte[1024*1024];
  private Hungry(){
  }
  private final static Hungry HUNGRY = new Hungry();
  public static Hungry getInstance(){
    return HUNGRY;
  }
}

2)DCL懒汉式

package single;
import java.lang.reflect.Constructor;
//懒汉式
public class LazyMan {
  private LazyMan(){
    synchronized(LazyMan.class){
      throw new RuntimeException("不要试图使用反射破坏异常");
    }
//    System.out.println(Thread.currentThread().getName()+"ok");
  }
  private static  LazyMan lazyMan;
  //双重检测锁
  public static LazyMan getInstance(){
    if (lazyMan==null) {
      synchronized (LazyMan.class) {
        if (lazyMan==null) {
          lazyMan = new LazyMan();
          //不是原子性操作
          //1.分配内存空间
          //2.执行构造方法,初始化对象
          //3.把这个对象指向空间
        }
      }
    }
    return lazyMan;
  }
  public static void main(String[] args) throws Exception {
//    LazyMan instance = LazyMan.getInstance();
    Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
    declaredConstructor.setAccessible(true);
    LazyMan instance = declaredConstructor.newInstance();
    LazyMan instance2 = declaredConstructor.newInstance();
    System.out.println(instance);
    System.out.println(instance2);
        for (int i = 0; i < 2; i++) {
      new Thread(()->{
        LazyMan.getInstance();
      }).start();
    }
  }
}

3)静态内部类

package single;
public class Holder {
  private Holder(){}
  private static Holder getInstance(){
    return InnerClass.HOLDER;
  }
  public static class InnerClass {
    private static final Holder HOLDER = new Holder();
  }
}

单例不安全, 主要的原因是因为反射。

4)枚举

package com.zmz.Singleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.Singleton
 * @ClassName: EnumSingle
 * @Author: 张晟睿
 * @Date: 2021/10/12 20:06
 * @Version: 1.0
 */
//enum 是什么? enum本身就是一个Class 类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}
class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        //java.lang.NoSuchMethodException: com.ogj.single.EnumSingle.<init>()
        EnumSingle instance2 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

image.png

枚举类型的最终反编译源码:

public final class EnumSingle extends Enum
{
    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }
    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(com/ogj/single/EnumSingle, name);
    }
    private EnumSingle(String s, int i)
    {
        super(s, i);
    }
    public EnumSingle getInstance()
    {
        return INSTANCE;
    }
    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];
    static 
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
            INSTANCE
        });
    }
}

19、深入理解CAS

如果想要深入了解CAS,请跳转到以下文章

【高频面试】锁与CAS详解(⭐建议收藏)_

1)什么是CAS?

CAS(Compare And Swap比较并替换),包含三个值当前内存值(V)、预期原来的值(A)以及期待更新的值(B),这里我们先做简单的介绍后期会出一期博文进行单独介绍的。

package com.zmz.CAS;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.CAS
 * @ClassName: CasDemo01
 * @Author: 张晟睿
 * @Date: 2021/10/12 20:15
 * @Version: 1.0
 */
public class CasDemo01 {
    //CAS : compareAndSet 比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        //boolean compareAndSet(int expect, int update)
        //期望值、更新值
        //如果实际值 和 我的期望值相同,那么就更新
        //如果实际值 和 我的期望值不同,那么就不更新
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        //因为期望值是2020  实际值却变成了2021  所以会修改失败
        //CAS 是CPU的并发原语
        atomicInteger.getAndIncrement(); //++操作
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

我们点开AtomicInteger源码观察一下,可以发现

image.png

image.png

2)总结

CAS:比较当前工作内存中的值 和 主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环,使用的是自旋锁。

缺点:

  • 一次性只能保证一个共享变量的原子性
  • 循环会耗时
  • 它会存在ABA问题

什么是ABA问题?(就是我们所说的狸猫换太子)

image.png

package com.zmz.CAS;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.CAS
 * @ClassName: CasDemo02
 * @Author: 张晟睿
 * @Date: 2021/10/12 20:40
 * @Version: 1.0
 */
public class CasDemo02 {
    //CAS : compareAndSet 比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        //boolean compareAndSet(int expect, int update)
        //期望值、更新值
        //如果实际值 和 我的期望值相同,那么就更新
        //如果实际值 和 我的期望值不同,那么就不更新
        System.out.println(atomicInteger.compareAndSet(2021, 2020));
        System.out.println(atomicInteger.get());
        //因为期望值是2020  实际值却变成了2021  所以会修改失败
        //CAS 是CPU的并发原语
//        atomicInteger.getAndIncrement(); //++操作
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

20、原子引用

为了解决ABA问题,对应的思想:就是使用了乐观锁~

注意:

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

说明:对于Integer var = ?在-128 至127之间的赋值,Integer对象是在IntegerCache  .cache产生,会复用已有对象, 这个区间内的Integer值可以直接使用==进  行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。

package com.zmz.CAS;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.CAS
 * @ClassName: CasDemo03
 * @Author: 张晟睿
 * @Date: 2021/10/12 20:45
 * @Version: 1.0
 */
public class CasDemo03 {
    /**AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
     * 正常在业务操作,这里面比较的都是一个个对象
     */
    static AtomicStampedReference<Integer> atomicStampedReference = new
            AtomicStampedReference<>(1, 1);
    // CAS compareAndSet : 比较并交换!
    public static void main(String[] args) {
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp(); // 获得版本号
            System.out.println("a1=>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 修改操作时,版本号更新 + 1
            atomicStampedReference.compareAndSet(1, 2,
                    atomicStampedReference.getStamp(),
                    atomicStampedReference.getStamp() + 1);
            System.out.println("a2=>" + atomicStampedReference.getStamp());
            // 重新把值改回去, 版本号更新 + 1
            System.out.println(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(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet(1, 3,
                    stamp, stamp + 1));
            System.out.println("b2=>" + atomicStampedReference.getStamp());
        }, "b").start();
    }
}

image.png

21、各种锁的理解

1)公平锁,非公平锁

  1. 公平锁:非常公平的锁,不能插队,必须先来后到image.png
  2. 非公平锁:非常不公平,允许插队,可以改变顺序image.png

2)可重入锁

image.png

1.Synchonized锁

package com.zmz.lock;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.lock
 * @ClassName: SynchonizedDemo
 * @Author: 张晟睿
 * @Date: 2021/10/12 20:59
 * @Version: 1.0
 */
public class SynchonizedDemo {
    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()+"=> 发短息");
        call();//这里也有一把锁
    }
    public synchronized void call(){
        System.out.println(Thread.currentThread().getName()+"=> 打电话");
    }
}

2.Lock锁

package com.zmz.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.lock
 * @ClassName: LockDemo
 * @Author: 张晟睿
 * @Date: 2021/10/12 21:02
 * @Version: 1.0
 */
public class LockDemo {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();
        new Thread(()->{
            phone.sms();
        },"A").start();
        new Thread(()->{
            phone.sms();
        },"B").start();
    }
}
class Phone2{
    Lock lock=new ReentrantLock();
    public void sms(){
        lock.lock(); //细节:这个是两把锁,两个钥匙
        //lock锁必须配对,否则就会死锁在里面
        try {
            System.out.println(Thread.currentThread().getName()+"=> 发短信");
            call();//这里也有一把锁
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void call(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "=> 打电话");
        }catch (Exception e){
            e.printStackTrace();
        }
        finally {
            lock.unlock();
        }
    }
}

注意:

  1. lock锁必须配对,相当于lock和 unlock 必须数量相同
  2. 在外面加的锁,也可以在里面解锁;在里面加的锁,在外面也可以解锁

3)自旋锁

1.spinlock

public final int getAndAddInt(Object var1, long var2, int var4) {    int var5;    do {        var5 = this.getIntVolatile(var1, var2);    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));    return var5;}

2.自我设计自旋锁

package com.zmz.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.lock
 * @ClassName: MySpinlockTest
 * @Author: 张晟睿
 * @Date: 2021/10/12 21:07
 * @Version: 1.0
 */
class MySpinlock {
    // 默认
    // int 0
    //thread null
    AtomicReference<Thread> atomicReference=new AtomicReference<>();
    //加锁
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"===> mylock");
        //自旋锁
        while (!atomicReference.compareAndSet(null,thread)){
            System.out.println(Thread.currentThread().getName()+" ==> 自旋中~");
        }
    }
    //解锁
    public void myUnlock(){
        Thread thread=Thread.currentThread();
        System.out.println(thread.getName()+"===> myUnlock");
        atomicReference.compareAndSet(thread,null);
    }
}
public class MySpinlockTest {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();
        reentrantLock.unlock();
        //使用CAS实现自旋锁
        MySpinlock spinlockDemo=new MySpinlock();
        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        },"Thread1").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        },"Thread2").start();
    }
}

运行结果:t2进程必须等待t1进程Unlock后,才能Unlock,在这之前进行自旋等待。

4)死锁

image.png

package com.zmz.lock;
import java.util.concurrent.TimeUnit;
/**
 * @ProjectName: Juc
 * @Package: com.zmz.lock
 * @ClassName: DeadLockDemo
 * @Author: 张晟睿
 * @Date: 2021/10/12 21:13
 * @Version: 1.0
 */
public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA= "lockA";
        String lockB= "lockB";
        new Thread(new MyThread1(lockA,lockB),"Thread1").start();
        new Thread(new MyThread1(lockB,lockA),"Thread2").start();
    }
}
class MyThread1 implements Runnable{
    private String lockA;
    private String lockB;
    public MyThread1(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);
            }
        }
    }
}

image.png

🤡我们如何去解决死锁的问题?

1、使用jps定位进程号,jdk的bin目录下: 有一个jps

jps -l

image.png

2、进程进程号 找到死锁信息

jstack 进程号  #如果出现拒绝访问  一定要以管理员身份运行

image.png

一般情况信息在最后:

image.png


希望对各位的面试有帮助,也希望小伙伴们多多支持厂长,留下你们的爱心💕和赞👍!

最后厂长祝大家1024程序员节快乐,拿到心仪的Offer


目录
相关文章
|
2天前
|
并行计算 算法 安全
面试必问的多线程优化技巧与实战
多线程编程是现代软件开发中不可或缺的一部分,特别是在处理高并发场景和优化程序性能时。作为Java开发者,掌握多线程优化技巧不仅能够提升程序的执行效率,还能在面试中脱颖而出。本文将从多线程基础、线程与进程的区别、多线程的优势出发,深入探讨如何避免死锁与竞态条件、线程间的通信机制、线程池的使用优势、线程优化算法与数据结构的选择,以及硬件加速技术。通过多个Java示例,我们将揭示这些技术的底层原理与实现方法。
29 3
|
1月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1天前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
1天前
|
Java 调度
|
3月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
|
3月前
|
消息中间件 前端开发 NoSQL
面试官:线程池遇到未处理的异常会崩溃吗?
面试官:线程池遇到未处理的异常会崩溃吗?
86 3
面试官:线程池遇到未处理的异常会崩溃吗?
|
4天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
14 1
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
60 1
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
32 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
25 2