JUC多线程并发 先放着 怕以后忘了

JUC多线程并发

1、JUC是什么?

java.util.concurrent在并发编程中使用的工具类。

2、进程和线程

​ 线程、进程,如果不能使用一句话说出来的技术不扎实。

进程:一个程序可以包含多个线程,程序的集合。

线程:开了一个进程。一个线程负责一个功能。 自动保存啥的(线程负责的)

JAVA:Thread、Runnable、Callable

java真的可以开启线程吗 不可以

本地方法,底层的是C++,java无法直接操作硬件

请添加图片描述

并发、并行

System.out.println(Runtime.getRuntime().availableProcessors());

8个

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ShCGB1Xv-1663771092986)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210209160520912.png)]

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

线程有几个状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSxgeR5a-1663771092987)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210209161009040.png)]

wait和sleep

1、来自不同的类

wait=>Object

sleep=>Thread

企业中。休眠,

(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210209161202297.png)]

2、关于锁的释放

wait会释放锁,

sleep抱着锁睡觉,不会释放

3、适用范围不同

wait

sleep

在这里插入图片描述

4、是否需要捕获异常

wait 不需要

sleep 需要

3、Lock锁(重点)

传统Synchronized

线程是一个单独的资源类,没有任何附属的才足以,

多线程操作一个资源。资源类是OOP

OOP资源类
在这里插入图片描述

1、LOCK锁

LOCK锁

在这里插入图片描述

2、公平锁和非公平锁

在这里插入图片描述

在这里插入图片描述

Synchronized和Lock区别

  1. Synchronized 内置的java关键字,lock是一个java类

  2. Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁

  3. Synchronized 会自动释放锁,自动挡。Lock必须要手动释放锁,如果不释放锁,死锁

  4. Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等);lock锁就不一定会等待下去。 lock.trylock()

  5. Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平么(可以自己设置)

  6. Synchronized 适合锁少量的代码问题,Lock适合锁大量的同步代码。

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

4、生产者和消费者问题

Synchronized版 wait notify

juc lock

大有门道

在这里插入图片描述

判断用while 不能用if ,超过两个线程就不安全

在这里插入图片描述

面试:单例模式,排序算法,生产者和消费者,死锁

问题存在。A B C D 4个线程!虚假唤醒


在这里插入图片描述

If 改为 while if 判断一次 while一直判断

JUC版的生产者和消费者问题

在这里插入图片描述

通过Lock 找到Condition

在这里插入图片描述

快捷键-----

在这里插入图片描述

在这里插入图片描述

任何一个新的技术,绝对不是仅仅只覆盖原来的技术,也带了优势和补充

Condition 精确的通知和唤醒线程

在这里插入图片描述

精准唤醒

在这里插入图片描述

5、8锁现象

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

对象,Class

小结

new this 具体的一个手机

static Class 唯一的模板

6、集合类不安全

java.util.ConcurrentModificationException 并发修改异常

List 不安全

List 不安全

三个安全方法

1.vector synchronized

2.collections.synchronizedList

3.CopyOnWriteArrayList lock

在这里插入图片描述

cow 优化策略

set 不安全

set 不安全

1.collections.synchronizedSet

2.copyonwriteArraySet

在这里插入图片描述

hashSet的底层是什么?

//无参构造
public HashSet() {
    map = new HashMap<>();
}
//添加  add set 本质就是map key 是无法重复的
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
PRESENT   //不变的值
private static final Object PRESENT = new Object();
//

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

map 不安全

map 不安全

也有collections.synchronizedMap
在这里插入图片描述

在这里插入图片描述

7、Callable(简单)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

细节:

1、有缓存

2、结果可能需要等待,会阻塞!

8、常用的辅助类

8.1、CountDownLatch

在这里插入图片描述

在这里插入图片描述

原理:

CountDownLatch.countDown() //数量 减1

CountDownLatch.await() //等待计数器归零,然后再向下执行
在这里插入图片描述

8.2、CyclicBarrier

加法计数器

在这里插入图片描述

在这里插入图片描述

8.3、Semaphore

信号量

在这里插入图片描述

在这里插入图片描述

semaphore acquire 假设满了。 等待,等待被释放为止

semaphore.release 释放量+1,然后唤醒等待的线程
在这里插入图片描述

9、读写锁 ReadWriteLock

独占锁(写锁) 一次只能被一个线程占有

共享锁(读锁)多个线程可以同时占有

ReadWriteLock

读-读 可以共存

读-写 不能共存

写-写 不能共存

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

在这里插入图片描述

10、阻塞队列 BlockingQueue

在这里插入图片描述

阻塞

BlockingQueue

BlockingQueue 不是新东西
在这里插入图片描述

Queue

在这里插入图片描述

队列

在这里插入图片描述

什么情况会使用阻塞队列?

多线程并发,线程池用队列维护。

学会使用队列

添加、移除

四组API

方式抛出异常有返回值阻塞等待超时等待
添加addoffer()put()offer(…)
移除removepoll()take()poll(…)
检测队首元素elementpeek

1、抛出异常

在这里插入图片描述

2、不会抛出异常

在这里插入图片描述

3、阻塞等待

在这里插入图片描述

4、超时等待

在这里插入图片描述

SynchronousQueue 同步队列

SynchronousQueue 同步队列

没有容量。

进去一个元素,必须等待取出来以后,才能再往里面放一个元素。

put take

在这里插入图片描述

在这里插入图片描述

11、线程池(重点) ThreadPool

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

池化技术

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

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

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

线程池的好处:

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

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

1、三大方法

线程池:三大方法

在这里插入图片描述

使用Executors 容易OOM

FixedThreadPool 固定线程

SingleThreadPool 单线程

请求过多, Interger.MAX_VALUE 约21亿

堆积大量请求OOM

CacheThreadPool 伸缩 和 ScheduledThreadPool

允许创建的线程数量过大,约21亿,创建大量线程导致OOM

package com.junlin.lock8;

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

public class Demo1 {
    public static void main(String[] args) {
//         ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个
//         ExecutorService threadPool = Executors.newFixedThreadPool(5);//固定
         ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩
        try {
            for (int i = 1; i <= 10; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" OK ");
                });

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

在这里插入图片描述

2、7大参数

7大参数

源码分析

    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

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

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

7大参数 的 银行例子
在这里插入图片描述

3、手动创建一个线程池

手动创建一个线程池

在这里插入图片描述

四种拒绝策略

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8J9nT6Kz-1663771093016)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210223122719961.png)]

4、小结

小结

最大线程 该如何定义

  1. CPU密集型,几核就是设置几,可以保持CPU的效率最高
获取CPU的核数。
  1. IO密集型
> 大于 判断 你程序中十分耗IO的线程

池的最大大小该如何去设置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNCkvRXl-1663771093017)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210223132830441.png)]

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

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

函数式接口 为了 简化 编程模型!!!!!!!!

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

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
// 泛型,枚举,反射,和注解 
//lambda表达式、链式编程、函数式接口、Stream流式计算

//超级多FunctionalInterface
//简化编程模型,在新版本的框架底层大量应用
//foreach(消费者类的函数式接口)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y71OooZU-1663771093017)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210223133212372.png)]

1、Function 函数型接口

在这里插入图片描述

package com.junlin.function;

import java.util.function.Function;

/**
 * Function 函数型接口  有一个输入参数,有一个输出
 * 只要是函数型接口,可以用lambda表达式简化
 */
public class Demo1 {
    public static void main(String[] args) {
//        Function function = new Function<String,String>() {
//            @Override
//            public String apply(String o) {
//                return o;
//            }
//        };  //匿名表达类
        Function function = (str)->{return  str;}; //lambda 表达式
//        Function function = str->str;  //  继续  简化
        System.out.println(function.apply("23423"));
    }
}

2、Predicate

public interface Predicate<T> {
    boolean test(T t);
}

//应用
package com.junlin.function;

import java.util.function.Predicate;

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

3、Consumer 消费型接口

只花。

//只有传入,没有返回值

@FunctionalInterface
public interface Consumer<T> {
    //只有传入,没有返回值
    void accept(T t);
}


package com.junlin.function;
/**
 * Consumer 消费型接口 : 只有输入,没有返回值
 */

import java.util.function.Consumer;

public class Demo3 {
    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String o) {
//                System.out.println(o);
//            }
//        };
        Consumer<String> consumer = x-> System.out.println(x);
        consumer.accept("345");
    }
}

4、Supplier 供给型接口

只给。

只有返回值,没有传值

供给型接口 只供给,不消费。

@FunctionalInterface
public interface Supplier<T> {
    T get();
}


package com.junlin.function;

import java.util.function.Supplier;

/**
 * Supplier  供给型接口   只供给,不消费。
 */
public class Demo4 {
    public static void main(String[] args) {
//        Supplier<String> supplier = new Supplier<String>() {
//            @Override
//            public String get() {
//                return "1024";
//            }
//        };
        Supplier<String> supplier = ()->"1024";
        System.out.println(supplier.get());
    }
}

13、Stream流式计算

什么是Stream流式计算

大数据:存储+计算

集合、Mysql 本质就是存储东西的;

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-laVM0OJH-1663771093018)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210223150127661.png)]

在这里插入图片描述

14、ForkJoin

什么是ForkJoin

分支合并

ForkJoin 在JDK1.7,并行执行任务!提高效率!大数据量!

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

分而治之

在这里插入图片描述

ForkJoin 特点 工作窃取
在这里插入图片描述

代码测试

在这里插入图片描述

  • 整体上 ForkJoinPool 是对 ThreadPoolExecutor 的一种补充
  • ForkJoinPool 提供了其独特的线程工作队列绑定方式工作分离以及窃取方式
  • ForkJoinPool + ForkJoinTask 配合实现了 Fork/Join 框架
  • 适用于任务可拆分为更小的子任务的场景(有点类似递归),适用于计算密集型任务,可以充分发挥 CPU 多核的能力

不同方法!!!!!!!!!!!

比较:

3000 普通程序员:

总和:500000000500000000-------耗时:436

6000 forkJoin 程序员:

总和:500000000500000000-------耗时:238

9000 程序员 流式并行计算:

总和:500000000500000000-------耗时:166

1、3000 普通程序员

代码:

    //3000 普通程序员

    public static  void test1(){
        long start = System.currentTimeMillis();
        long sum = 0l;
        for (long i = 1l; i <= 10_0000_0000; i++) {
            sum+=i;
        }
        long end = System.currentTimeMillis();
        System.out.println("总和:"+sum+"-------"+"耗时:"+(end - start));
    }

时间和结果:

总和:500000000500000000-------耗时:436

2、6000 forkJoin 程序员

方法继承RecursiveTask

package com.junlin.forkjoin;

import java.util.concurrent.RecursiveTask;

/**
 * RecursiveTask  有返回值的任务
 * RecursiveAction  没有返回结果的任务
 * CountedCompleter: 在任务完成执行后,触发自定义的钩子函数。
 *  1.forkjoinpool  通过它来执行
 *  2.计算任务 forkjoinpool.execute(forkjoinTask task)
 *  3.计算类要继承  forkjoinTask
 */
public class ForkJoinDemo1 extends RecursiveTask<Long> {
    private Long Start;
    private Long End;
    private Long Max = 10000L;

    //构造函数  丢入 首尾数字
    public ForkJoinDemo1(Long Start,Long End){
        this.Start = Start;
        this.End = End;
//        this.Max = Max;
    }
    @Override
    protected Long compute() {
        if((End - Start) < Max){
            long sum = 0L;
            for (long i = Start; i <= End ; i++) {
                  sum += i;
            }
           return sum;
        }else {
            //否则就进入 分支合并 forkJoin
            long middle = (End + Start)/2;
            //任务一分为2    任务1
            ForkJoinDemo1 task1 = new ForkJoinDemo1(Start,middle);
            //拆分任务,把任务压入线程队列
            // 内部入队 和外部入队  用 ForkJoinPool维护 工作队列
            task1.fork();
            //任务2,task2
            ForkJoinDemo1 task2 = new ForkJoinDemo1(middle+1,End);
            task2.fork();
            //爆红 缺少 约束
            return task1.join() + task2.join();
        }
    }
}

实现test

    //6000 程序员 forkjoin
    public static  void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        //new 一个 ForlJoinPool
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo1(0l,10_0000_0000l);
        //forkJoinPool.execute();  没有返回值
        //forkJoinPool.submit 有返回值 可以get
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        //提交后 get
        long sum = submit.get();

        long end = System.currentTimeMillis();

        System.out.println("总和:"+sum+"-------"+"耗时:"+(end - start));
    }
//声明 ForkJoinPool 。
//继承实现 ForkJoinTask 抽象类或其子类,在其定义的方法中实现你的业务逻辑。
//子任务逻辑内部在合适的时机进行子任务的 fork 拆分。
//子任务逻辑内部在合适的时机进行 join 汇总

总和:500000000500000000-------耗时:238

3、9000 Stream流式计算

使用 longstream

parallel 并行的意思

parallel 并行

java8除了新增stream,还提供了parallel stream-多线程版的stream,parallel stream的优势是:充分利用多线程,提高程序运行效率,

reduce 归纳 归约运算 identity 初始值 本体。

    //9000 程序员 流式并行计算
public static  void test3(){
    long start = System.currentTimeMillis();
    //intStream  doubleStream longStream
    //range() 闭区间  rangeClosed 左闭右开
    long sum = LongStream.rangeClosed(1L, 10_0000_0000L).parallel().reduce(0, Long::sum);
    long end = System.currentTimeMillis();
    System.out.println("总和:"+sum+"-------"+"耗时:"+(end - start));
}

结果如下

总和:500000000500000000-------耗时:166

15、异步回调 Future

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

可以做很多事。

CompletableFuture 实现类

completable 可完备化的;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u5qP3ZZI-1663771093021)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210223211801465.png)]

在这里插入图片描述

package com.junlin.future;

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

public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep((2));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "runAsync=>void");
        });
        
        System.out.println("11111");
        
        completableFuture.get();//获取阻塞结果
    }
}

16、JMM

请你谈谈对Volatile的理解

Volatile是Java虚拟机提供轻量级的同步机制 易变的; 无定性的; 无常性的; 可能急剧波动的; 不稳定的; 易恶化的; 易挥发的; 易发散的

1、保证可见性

2、不保证原子性

3、禁止指令重排

什么是JMM

JMM: java 内存模型,不存在的东西,只是一个概念!亦是约定!

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

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

8种操作

在Java中JMM内存模型定义了八种操作来实现同步的细节。

  • read 读取,作用于主内存把变量从主内存中读取到本本地内存。
  • load 加载,主要作用本地内存,把从主内存中读取的变量加载到本地内存的变量副本中
  • use 使用,主要作用本地内存,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。、
  • assign 赋值 作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store 存储 作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
  • write 写入 作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
  • lock 锁定 :作用于主内存的变量,把一个变量标识为一条线程独占状态。
  • unlock 解锁:作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。

所以看似简单的通信其实是这八种状态来实现的。

同时在Java内存模型中明确规定了要执行这些操作需要满足以下规则:

  • 不允许read和load、store和write的操作单独出现。
  • 不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中。
  • 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中。
  • 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作。
  • 一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现
  • 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值
  • 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
  • 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。

所以上面说的操作要严格执行。

问题:

程序不知道 主内存的num 被修改过了 所以一直还在执行。

package com.junlin.tvolatile;

import java.util.concurrent.TimeUnit;

public class Demo01 {
    private  static int num = 0;
    public static void main(String[] args) {

        new Thread(()->{
            while(num==0){

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

在这里插入图片描述

17、Volatile

1、保证可见性

是指 保证 线程A 可见。主存的变量发生了变化。同步机制之一。

package com.junlin.tvolatile;

import java.util.concurrent.TimeUnit;

public class Demo01 {
    private  volatile static int num = 0;
    //不加volatile  一直 死循环
    //加了 volatile 立马结束。  可见性。
    public static void main(String[] args) {

        new Thread(()->{
            while(num==0){

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

2、不保证原子性

原子性:不可分割

线程A在执行任务的时候,不能被打扰,也不能被分割。要么同时成功,要么同时失败

volatile

package com.junlin.tvolatile;

public class VDemo02 {
    //volatile 不影响 最后结果 仍为18000左右
    private volatile static   int num = 0;

    //可以在add 方面 加 synchronized  num 为2 0000
    private  static void  add(){
        num ++;
    }
    public static void main(String[] args) {
        //理论上 结果是 2 0000
        for (int i = 1; i <= 20 ; i++) {
            new Thread(()->{
                for (int j = 1; j <=1000 ; j++) {
                    add(); //add 不加static  静态方法  无法调用
                }
            }).start();
        }
        //不加 while 礼让   num    15000  4000 浮动很大
        //加了 礼让 num  为  18280   17641   16000以上。
        while(Thread.activeCount() > 2){ //main  gc 两个线程
            Thread.yield();//礼让 一起跑 直到跑完
        }
        System.out.println(num);
    }
}

简单的add 可以通过反编译方法。 先在 idea,生成的target文件,找到文件位置。直接在目录输入 cmd 。在当前位置打开cmd

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

通过 javap -c VDemo.class 反编译

C:\Users\junlin\IdeaProjects\lockTest\target\classes\com\junlin\tvolatile>javap -c VDemo02.class

在这里插入图片描述

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

用了 lock 锁不住。。。无论是 add 方法 还是 main 方法。

在这里插入图片描述

使用原子类 解决原子性的问题
在这里插入图片描述

​ AtomicInteger 类。 结果也为 2 0000

num.getAndIncrement(); //AtomicInteger +1 方法 CAS

package com.junlin.tvolatile;


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

public class VDemo02 {
    //volatile 不影响 最后结果 仍为18000左右
    private volatile static AtomicInteger num = new AtomicInteger(0);

    //可以在add 方面 加 synchronized  num 为2 0000
    public  static void  add(){
            num.getAndIncrement(); //不是一个原子性操作
    }

    public static void main(String[] args) {

        //理论上 结果是 2 0000

        for (int i = 1; i <= 20 ; i++) {
            new Thread(()->{
                for (int j = 1; j <=1000 ; j++) {
                    add(); //add 不加static  静态方法  无法调用
                }
            }).start();
        }
        //不加 while 礼让   num    15000  4000 浮动很大
        //加了 礼让 num  为  18280   17641   16000以上。
        while(Thread.activeCount() > 2){ //main  gc 两个线程
            Thread.yield();//礼让 一起跑 直到跑完
        }
        System.out.println(num);

    }
}

这些类的底层都直接和操作系统挂钩!

在内存中修改值!

Unsafe是一个很特殊的存在!!!

3、指令重排

什么是指令重排 :

你写的程序,计算机并不是按照你写的那样去执行的。

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

处理器在进行指令重排的时候,考虑:数据之间的依赖性! HB原则。happen before

先前发生的事件 对 后面的事件具有可见性

在这里插入图片描述

非计算机专业

volatile可以避免指令重排:

内存屏障 也就是个CPU指令

作用:

1、保证特定的操作的执行顺序!

2、可以保证某些变量的内存可见性(利用这些特性,volatile 可先了可见性)
在这里插入图片描述

Volatile 是可以保持 可见性。

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

volatile 的内存屏障在 单例模式中 应用最多

饿汉式

DCL 懒汉式

18、彻底玩转单例模式

Double Check Lock DCL懒汉式 双重检测锁机制

1、饿汉式单例

空间多 换时间 少

(静态常量 后一点; 静态代码块。执行顺序优先。)

优点:

  1. 没有加任何的锁、执行效率比较高
  2. 用户体验比懒汉式好
  3. 使用的是ClassLoader ,里面代码是加了synchronized。 同步的问题,线程安全。

缺点:

​ 全部加载,可能浪费内存。

没有使用这个实例。就会觉得浪费

package com.junlin.single;

//饿汉式单例
public class Hungry {
    //单例  构造器私有
    private Hungry(){

    }
    /**
     * 2.类中创建对象----但是要在静态代码块中创建
     * 这里要说一下final,作用是不允许被修改变量的值,能修改值的时机是静态代码块以及有参构造加载时。
     * 这个参数 是内部构造出来的 hungry
     */
    private final static Hungry hungry = new Hungry();

    //给一个外部接口。获得单例
    public static Hungry getInstance() {
        return hungry;
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        getInstance();
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

2、懒汉式单例

时间多(加锁,还有获取实例判断) 换 少空间

2.1 懒汉式 第一版

单线程安全,多线程是不安全的。

package com.junlin.single;

//懒汉式单例 第一版
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    //初始化lazyMan 不赋值
    private static  LazyMan lazyMan;

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

    public static void main(String[] args) {
        for (int i = 1; i <= 10 ; i++) {
            new Thread(()->{
                getInstance();
            }).start();
        }
    }
}
2.2 懒汉式 第二版 (DCL 双重检测锁)懒汉式
package com.junlin.single;

//懒汉式单例 第一版
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    //初始化lazyMan 不赋值
    private static  LazyMan lazyMan;

    //双重检测锁机制 DCL
    public static LazyMan getInstance() {
        if(lazyMan == null){
            //给class  加锁  比同步方法性能更高。已有对象,无需进入同步代码块。
            synchronized (LazyMan.class) {
                if (lazyMan == null)
                    lazyMan = new LazyMan();
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 10 ; i++) {
            new Thread(()->{
               getInstance();
            }).start();
        }
    }
}
2.3 懒汉式 第三版 volatile 禁止指令重排
package com.junlin.single;

//懒汉式单例 第一版
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"ok");
    }
    //初始化lazyMan 不赋值
    private volatile static  LazyMan lazyMan;

    //双重检测锁机制 DCL
    public static LazyMan getInstance() {
        if(lazyMan == null){
            //给class  加锁  比同步方法性能更高。已有对象,无需进入同步代码块。
            synchronized (LazyMan.class) {
                if (lazyMan == null)
                    lazyMan = new LazyMan();//不是一个原子性操作
                /**
                 * 1. 分配内存空间
                 * 2. 执行构造方法,初始化对象
                 * 3. 把这个对象指向这个空间
                 *
                 * 123
                 * 132 A B 两个线程。 A完成,B还没完成,此时lazyman 还没有完成构造 就会造成干扰。需要加volatile
                 */

            }
        }
        return lazyMan;
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 10 ; i++) {
            new Thread(()->{
               getInstance();
            }).start();
        }
    }
}

3、静态内部类 实现单例

优点:classloader机制,避免了线程不安全。同时也是利用静态内部类特点实现延迟加载,效率高。

外部类加载顺序:
1、外部类初次加载,会初始化静态变量、静态代码块、静态方法,但不会加载内部类和静态内部类。
2、实例化外部类,调用外部类的静态方法、静态变量,则外部类必须先进行加载,但只加载一次。
3、直接调用静态内部类时,外部类不会加载。

package com.junlin.single;
//静态内部类
public class Holder {
    //静态内部类 比饿汉好。可以线程安全且懒加载。比懒汉好,可以懒加载,且线程安全
    private Holder(){

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

4、用反射打败 (饿汉,懒汉,静态内部类)

问题:

1、已有对象,用反射创造第二个对象。

2、不用对象,直接反射创造两个对象。

在这里插入图片描述

3、使用反编译破解信号量加密

在这里插入图片描述

4.1、私有构造函数 加锁
package com.junlin.single;

import com.sun.imageio.plugins.common.LZWCompressor;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

//用反射打败  第一版
public class LazyMan {
    private LazyMan() {
        //私有构造函数 加锁,锁类。不为空报错,不允许继续构造
        synchronized (LazyMan.class) {
            if (lazyMan != null) {
                throw new RuntimeException("不要试图使用反射破坏异常!!");
            }
        }
        System.out.println(Thread.currentThread().getName() + "ok");
    }

    //初始化lazyMan 不赋值
    private volatile static LazyMan lazyMan;
    //双重检测锁机制 DCL
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            //给class  加锁  比同步方法性能更高。已有对象,无需进入同步代码块。
            synchronized (LazyMan.class) {
                if (lazyMan == null)
                    lazyMan = new LazyMan();
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);

        LazyMan newInstance = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(newInstance);

    }
}

4.2、通过信号量的加密。阻止非破解的反射。
package com.junlin.single;

import com.sun.imageio.plugins.common.LZWCompressor;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

//反射破解单例 第二版
public class LazyMan {
    //做一个信号量的标志
    private static  boolean junlin = false;
    private LazyMan() {
        //私有构造函数 加锁,锁类。不为空报错,不允许继续构造
        synchronized (LazyMan.class) {
            if (junlin==false) {
                junlin =true;
            }else{
                throw new RuntimeException("不要试图使用反射破坏异常!!");
            }
        }
        System.out.println(Thread.currentThread().getName() + "ok");
    }

    //初始化lazyMan 不赋值
    private volatile static LazyMan lazyMan;

    //双重检测锁机制 DCL
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            //给class  加锁  比同步方法性能更高。已有对象,无需进入同步代码块。
            synchronized (LazyMan.class) {
                if (lazyMan == null)
                    lazyMan = new LazyMan();//不是一个原子性操作
            }
        }
        return lazyMan;
    }
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

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

        LazyMan newInstance = declaredConstructor.newInstance();
        LazyMan newInstance2 = declaredConstructor.newInstance();

        System.out.println(newInstance);
        System.out.println(newInstance2);
    }
}

5、使用枚举类实现单例

通过源码查看

if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");

使用反射破解。

报错。没有类似方法。

但是 idea 中 target 生成的enum方法 显示 无参构造。 idea 骗了我们 。

在这里插入图片描述

去源码分析:

C:\Users\junlin\IdeaProjects\lockTest\target\classes\com\junlin\single>javap -p EnumSingle.class
Compiled from "EnumSingle.java"
public final class com.junlin.single.EnumSingle extends java.lang.Enum<com.junlin.single.EnumSingle> {
  public static final com.junlin.single.EnumSingle INSTANCE;
  private static final com.junlin.single.EnumSingle[] $VALUES;
  public static com.junlin.single.EnumSingle[] values();
  public static com.junlin.single.EnumSingle valueOf(java.lang.String);
  private com.junlin.single.EnumSingle();
  public com.junlin.single.EnumSingle getInstance();
  static {};
}

private com.junlin.single.EnumSingle(); 也是无参构造

javap 文件分解。反编译

通过javap -p 查看 显示所有类和成员。

下载 jad

使用命令 jad -sjava xx.class

5.1、枚举类 反编译源码
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingle.java

package com.junlin.single;


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

发现。

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

有一个string,和一个int 构造。

package com.junlin.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return  INSTANCE;
    }
}

class  Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance = EnumSingle.INSTANCE;
//        EnumSingle instance2 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        EnumSingle newInstance = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(newInstance);

    }
}

Exception in thread "main" java.lang.NoSuchMethodException: com.junlin.single.EnumSingle.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at com.junlin.single.Test.main(EnumSingle.java:17)

Process finished with exit code 1

构造函数 放入 string,和int

class  Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance = EnumSingle.INSTANCE;
//        EnumSingle instance2 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle newInstance = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(newInstance);

    }
}

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.junlin.single.Test.main(EnumSingle.java:19)

19、深入理解CAS

什么是CAS

底层原理,为了精深必须会!

package com.junlin.cas;


import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    //CAS  compareAndSwap  比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2000);
        //compareAndSet  底层就是 compareAndSwap
        //public final boolean compareAndSet(int expect, int update)
        //如果我期望达到了,那么更新,否则就不更新。CAS 是CPU的并发原语!
        System.out.println(atomicInteger.compareAndSet(2000,2001));
        System.out.println(atomicInteger.get());

        System.out.println(atomicInteger.compareAndSet(2000,2001));
        System.out.println(atomicInteger.get());
    }
}

在这里插入图片描述

获取内存的偏移值

volatile 内存屏障。禁止指令重排。

在这里插入图片描述

且是一个自旋锁。

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

缺点:

1、循环会耗时

2、一次性只能保证一个共享变量的原子性

3、ABA问题

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

修改过有人捣乱

20、原子引用

乐观锁。 CAS

ABA问题

在这里插入图片描述

使用原子引用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n8QH1mvv-1663771093027)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210224222316759.png)]

在这里插入图片描述

在这里插入图片描述

Interger var = ? 值是 -128-127

21、各种锁理解

1、公平锁、非公平所

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

非公平所:非常不公平,可以插队(默认都是非公平)

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

2、可重入锁

可重入锁(递归锁)

在这里插入图片描述

Synchronized 版

package com.junlin.lock;

public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
//B需要等A解锁。所以输出结果是一对一对的
        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 版

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nwugrzMr-1663771093028)(C:\Users\junlin\AppData\Roaming\Typora\typora-user-images\image-20210224224300618.png)]

3、自旋锁

SpinLock

在这里插入图片描述

自己写的自旋锁,利用原子引用类型。CAS原理。

锁,先锁的必须先解锁。不然一直自旋。

package com.junlin.lock;

import java.util.Timer;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁  需要CAS 用原子引用来实现
 * AtomicReference 是原子引用类型
 */
public class SpinLock {
    //int 0
    //Thread  null 引用类型
    AtomicReference<Thread> atomicReference =  new AtomicReference<>();

    //加锁
    public void  myLock()  {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"====> myLock");
        // 期望是null,不是null。就是false  !false 就是true  。。 就一直自旋  直到。null
        //!!!!!!!!!!!!很重要
        while(!atomicReference.compareAndSet(null,thread)){
//            System.out.println(Thread.currentThread().getName()+"还在自旋");
        }

    }
    public void myUnLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"====> myUnLock");
        //不用判断。直接更新。
        atomicReference.compareAndSet(thread,null);

    }
}

Test代码

package com.junlin.lock;

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

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

        //底层使用的自旋锁CAS
        SpinLock lock = new SpinLock();
        /**
         * T1线程  拿到锁,睡1--------------2--------------3秒 。T1 unlock
         *               T2睡一秒,也拿到锁。然后自旋。          T2自旋发现。
         *               (必须等T1的当前Thread引用,刚好满足期待然后unlock)
         */
        new Thread(()->{
            lock.myLock();//锁最好 放在 try catch finally 外面。
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        },"T1").start();

        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{

             lock.myLock();//锁最好 放在 try 外面。

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        },"T2").start();

    }
}

输出结果:

T1 ====> myLock
T2 ====> myLock
T1 ====> myUnLock
T2 ====> myUnLock

4、死锁

死锁是什么?

在这里插入图片描述

死锁测试,怎么排除死锁。

解决问题

1、使用jps定位进程号

jps -l

查看活着的进程

2、使用 jstack 进程号找到死锁问题

在这里插入图片描述

面试工作中!排查问题:

1、日志

2、堆栈

jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供bai的一du个显示当前zhi所有daojava进程pid的命令,简单实用,非zhuan常适合在linux/unix平台shu上简单察看当前java进程的一些简单情况

jstack(查看线程)、jmap(查看内存)和jstat(性能分析)

JVM性能调优监控工具

jps、 查看活着进程

jstack、 查看线程

jmap、jhat、 查看内存

jstat 性能分析

hread.currentThread();
System.out.println(Thread.currentThread().getName()+“====> myUnLock”);
//不用判断。直接更新。
atomicReference.compareAndSet(thread,null);

}

}




Test代码

```java
package com.junlin.lock;

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

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

        //底层使用的自旋锁CAS
        SpinLock lock = new SpinLock();
        /**
         * T1线程  拿到锁,睡1--------------2--------------3秒 。T1 unlock
         *               T2睡一秒,也拿到锁。然后自旋。          T2自旋发现。
         *               (必须等T1的当前Thread引用,刚好满足期待然后unlock)
         */
        new Thread(()->{
            lock.myLock();//锁最好 放在 try catch finally 外面。
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        },"T1").start();

        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{

             lock.myLock();//锁最好 放在 try 外面。

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        },"T2").start();

    }
}

输出结果:

T1 ====> myLock
T2 ====> myLock
T1 ====> myUnLock
T2 ====> myUnLock

4、死锁

死锁是什么?

[外链图片转存中…(img-54qukR46-1663771093029)]

死锁测试,怎么排除死锁。

[外链图片转存中…(img-wMR5xOCc-1663771093029)]

解决问题

1、使用jps定位进程号

jps -l

查看活着的进程

2、使用 jstack 进程号找到死锁问题

[外链图片转存中…(img-P8k83Uro-1663771093030)]

面试工作中!排查问题:

1、日志

2、堆栈

jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供bai的一du个显示当前zhi所有daojava进程pid的命令,简单实用,非zhuan常适合在linux/unix平台shu上简单察看当前java进程的一些简单情况

jstack(查看线程)、jmap(查看内存)和jstat(性能分析)

JVM性能调优监控工具

jps、 查看活着进程

jstack、 查看线程

jmap、jhat、 查看内存

jstat 性能分析

等使用详解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值