(大厂必备)厂长熬夜爆肝万字之多线程高并发JUC编程⭐学妹已收藏

Posted java厂长

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(大厂必备)厂长熬夜爆肝万字之多线程高并发JUC编程⭐学妹已收藏相关的知识,希望对你有一定的参考价值。

🔥(大厂必备)厂长熬夜爆肝万字之多线程高并发JUC编程(二)⭐学妹已收藏

❤️‍ 大家好,我是java厂长,今天再次带你们体验一把多线程高并发的面试高频!❤️‍

关于作者

  • 作者介绍

🍓 博客主页:作者主页

🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉🏆。

🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨‍💻。

JUC学习

15、异步回调

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

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

但是我们平时都使用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(解锁)作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定


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

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

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

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

使用原子类

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=ay=b
b=1a=2

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

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

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

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

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

volatile可以避免指令重排:

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

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

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(instance

以上是关于(大厂必备)厂长熬夜爆肝万字之多线程高并发JUC编程⭐学妹已收藏的主要内容,如果未能解决你的问题,请参考以下文章

熬夜爆肝万字C#基础入门大总结建议收藏

熬夜爆肝万字C#基础入门大总结建议收藏

建议收藏|熬夜爆肝万字文带你了解DOM,文末有彩蛋嗷!!!!✨✨✨

建议收藏|熬夜爆肝万字文带你了解DOM,文末有彩蛋嗷!!!!✨✨✨

[MySQL]爆肝万字,带你入门MySQL(SQL语句命令大全)

中秋节爆肝万字,带你熟悉Linux进程的基本操作!!!