【JUC并发编程】JUC必备知识总结

JUC概述

什么是JUC

JUC 是 java.util.concurrent工具包简称,是一个处理线程的工具包。JDK1.5开始出现

进程线程

进程:指系统中正在运行的一个应用程序,进程是CPU资源分配的最小单位
线程:系统分配处理器时间资源的基本单元,进程内独立执行的一个单元执行流。线程是程序执行的最小单元。

线程的状态

  1. NEW 新建
  2. RUNNABLE 准备就绪
  3. BLOCKED 阻塞
  4. WAITING 不见不散
  5. TIMED_WAITING 过时不候
  6. TERMINATED 终结

wait和sleep区别

  1. sleep是Thread的静态方法, wait是Object的方法,任何实例对象都能调用。
  2. sleep不会释放锁,也不需要占用锁。 wait会释放锁,但调用它的前提是当前线程占有锁(代码在synchronized中)
  3. 都可以被interrupted方法中断。

并发与并行

并发:同一时刻,多个线程访问同一个资源,多线程对一个点 【电商秒杀】
并行:多项工作一起执行,之后汇总。

管程

管程是Monitor 监视器,即锁,是一种同步机制,保证同一时间只有一个线程访问被保护的数据或代码。
JVM同步基于进入和退出,是使用管程对象进行管理的。

用户线程与守护线程

用户线程: 平时自定义的线程, 主线程结束,用户线程运行,JVM仍存活
守护线程: 比如垃圾回收线程, 只有守护线程没有用户线程了, JVM结束

Lock接口

synchroized 实现卖票例子
Lock的可重入锁实现卖票例子
Lock和synchroized 的不同

线程间通信

多线程编程步骤:①创建资源类,②判断、干活、通知 ③创建多线程调用资源类方法 ④防止虚假唤醒问题
线程间通信:两个线程交替完成加一减一例子
定制化通信,三个线程AA打印5次、BB打印10次、CC打印15次,循环10轮 例子

集合的线程安全

ArrayList不安全解决方式: ①vector ②Collections.synchronizedList() ③CopyOnWriteArrayList
HashSet不安全解决方式:CopyOnWriteArraySet
HashMap不安全解决方式:ConcurrentHashMap

多线程锁

锁的范围

Java中每个对象都可以作为锁。具体表现为以下三种形式:

  1. 对于普通同步方法,锁是当前实例对象this
  2. 对于静态同步方法,锁是当前类字节码,Class对象
  3. 对于同步方法块,锁是synchronized括号里配置的对象

公平锁与非公平锁

非公平锁 可能导致其他线程饿死。(不问有没有人,直接抢),效率高
公平锁 阳光普照(问一下有人没,有人就排队),效率相对低

可重入锁

synchronized() 是隐式的可重入锁, Lock是显式的可重入锁
可重入锁特点:可以自由进入多层锁。打开外层锁后,还可以继续打开内层锁。

死锁

什么是死锁

两个或两个以上的进程, 因为争夺资源而造成互相等待的现象,如果没有外力干涉,他们无法再执行下去

产生死锁的原因

  1. 系统资源不足
  2. 进程运行推进顺序不合适
  3. 资源分配不当
    手写死锁例子。

验证是否是死锁

  1. jps -l 得到正在运行的程序进程号
  2. jstack 进程号 可以验证是否是死锁发生

Callable接口

Runable接口和Callable接口区别

  1. 是否有返回值, Callable有返回值
  2. 是否抛出异常, 无法计算结果会有异常抛出
  3. 实现方法名称不同, Runable接口是run方法, Callable接口是call方法

想要通过Callable接口创建线程,必须得通过Runnable接口的实现类 FutureTask,FutureTask构造可以传递Callable,然后new Thread,放入FutureTask

FutureTask原理

举例:
1.老师上课口渴了没水喝,跑去买水不合适,讲课线程继续,单开启另外线程,让班长去买水,把水买回来放那,老师需要的时候直接get()
2.三个同学,三个计算任务,我是主线程需要统计他们计算的结果, 假设第二个同学计算量很大,我从第一个同学开始汇总,到了第二个同学,他还没计算好, 我单独给第二个同学开一个线程让他继续计算,我去汇总第三个同学。等我汇总完了第三个同学, 再回过头来等第二个同学计算完,并汇总。

JUC强大的辅助类

减少计数 CountDownLatch

CountDownLatch的构造方法,传入一个初始计数。 await方法是阻塞式的,计数为0才通过。 countDown() 不是阻塞的,会使计数减一
例子: 6个同学走完了,班长才锁门

循环栅栏CyclicBarrier

cyclicBarrier.await();没有达到设定的值的时候,不会执行await之后的语句,线程会阻塞,每次阻塞设定的值会+1, 到达后会执行cyclicBarrier对象的runnable接口,每个线程执行cyclicBarrier.await();后续的任务
例子: 集齐七颗龙珠即可召唤神龙

信号灯Semaphore

acquire() 方法,从此信号量中获取一个许可,在提供一个许可前线程将一直阻塞
release() 释放一个许可,将其返回给信号量

例子: 6辆车停进三个停车位

ReentrantReadWriteLock读写锁

悲观锁、乐观锁

悲观锁,不支持并发操作,每次操作都锁上。
乐观锁,支持并发操作,操作完改变版本号,如果其他线程操作提交时发现版本号变了就回滚。

表锁、行锁

表锁: 整张表都锁上
行锁: 只锁一行
行锁可能发生死锁

读写锁

读写锁都可能发生死锁

在这里插入图片描述

读写锁特点

并发读, 独占写, 读锁和写锁都可能发生死锁
一个资源可以被多个读线程访问,或者可以被一个写线程访问, 但是不能同时存在读写线程, 读写互斥、写写互斥、读读共享。
ps:读写锁本质上是一种自旋锁
举例: 读写锁缓存。

缺点:

  1. 造成锁饥饿,一直读,没法写操作, 比如坐地铁,100个人上车1个人下车,一直堵着下不了
  2. 读的时候,哪个线程都不可以写, 写的时候,自己线程可以读,其他线程不能读

写锁可以降级为读锁, 读锁不能升级为写锁
降级方法:1.获取写锁 2.获取读锁 3.释放写锁 4.释放读锁, 实时就完成了写锁降级为读锁的过程。

BlockingQueue阻塞队列

常用:

  1. ArrayBlockingQueue (常用) : 由数组结构组成的有界的阻塞队列
  2. LinkedBlockingQueue(常用) : 由链表组成的有界的阻塞队列,默认大小为最大整型值
    常用方法:
    第一组方法 抛出异常 add(e) remove() element()
    第二组方法 返回特殊值 offer(e) poll() peek()
    第三组方法 阻塞 put(e) take()
    第四组 阻塞,超时放弃 offer(e, time, unit) poll(time, unit)

ThreadPool 线程池

概述及架构

线程池特点:

  1. 可以降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度:任务到达时,可以不需要等待线程创建就能立即执行
  3. 提高线程的可管理性:线程是稀缺资源,无限制创建会消耗系统资源,降低系统稳定性。用线程池可以统一分配、调优和监控

架构–Executor框架实现

在这里插入图片描述

线程池使用方式

  1. 一池N线程: Executors.newFixedThreadPool(int)
  2. 一池一线程 : Executors.newSingleThreadExecutor()
  3. 可扩容线程池 :Executors.newCachedThreadPool()

底层都是用的ThreadPoolExecutor

ThreadPoolExecutor 七个参数含义

  1. int corePoolSize, 常驻线程数量(核心)
  2. int maximumPoolSize 最大线程数量
  3. long keepAliveTime,线程存活时间
  4. TimeUnit unit , 时间单位
  5. BlockingQueue workQueue ,阻塞队列
  6. ThreadFactory threadFactory, 线程工厂
  7. RejectedExecutionHandler handler,拒绝策略

线程底层工作流程

在这里插入图片描述
在执行execute()方法的时候才开始创建线程,根据需求先创建常驻线程,然后再来需求进入阻塞队列, 再来需求创建更多线程直接进行处理,再来需求执行拒绝策略

四种拒绝策略

  1. AbortPolicy (默认): 直接抛出异常,阻止系统正常运行
  2. CallerRunsPolicy: 不会抛弃任务,也不会抛出异常,而是将某些任务回退给调用者
  3. DiscardOldestPolicy: 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
  4. DiscardPolicy:不做任何处理

Fork/Join 分支合并

Fork: 把复杂任务进行拆分
Join: 把分拆的任务结果进行合并
例子:1+2+…+100 拆分分别计算再合并

CompletableFuture异步回调

CompletableFuture.runAsync() 没返回值的异步回调
CompletableFuture.supplyAsync() 有返回值的异步回调

猜你喜欢

转载自blog.csdn.net/weixin_44179010/article/details/122246498