一.:JUC 概述(1~4)
1:什么是 JUC:
1)JUC 简介:
-1:在 Java 中,线程部分是一个重点,本篇文章说的 JUC 也是关于线程的。
-2:JUC 就是 java.util.concurrent 工具包的简称。
-3:这是一个处理线程的工具包,jdk1.5 出现的。
2:线程 和 进程的概念:
1)进程 & 线程:
-1:指 在系统中,正在运行的一个应用程序;程序一旦运行就是进程。
(进程 — 资源分配的最小单位)
-2:操作系统分配处理器时间资源的基本单元。或者说,进程之内独立执行的一个单元执行流。
(线程 — 程序执行的最小单位)。
2)线程的状态:(流程图)
3)wait & sleep:
-1:相同:
a:一旦执行方法,都可以使当前线程进入阻塞状态。
b:他们都可以被 interrupted 方法中断。
-2:不同:
a:两个方法声明的位置不同:
sleep() --> Thread 类中声明的静态方法。
wait() --> Object 类中声明的方法,任何对象都可以调用。
b:调用的范围不同:
sleep() --> 任意线程 的 任意位置 都可以调用。
wait() --> 必须使用在 同步方法 或 者同步代码块 中。
c:关于是否释放同步监视器问题:
(如果两个方法都是用在,同步代码块 或者 同步方法中)
sleep() --> 不会释放同步锁,它也不会占锁。
wait() --> 释放同步锁
4)并发 & 并行:
-1:并发:一个 CPU 同时执行多个任务。(同一时刻多个线程访同一资源)
-2:并行:多个 CPU 同时执行多个任务。(多个线程并行执行,之后再汇总)
5)用户线程 和 守护线程:
-1:用户线程:他们在几乎每个方面都是相同的,唯一的区别是 判断 JVM 何时离开。
(主线程结束了,用户线程还在运行,JVM 存活)
-2:守护线程:
1、守护线程 是用来服务 用户线程的,通过 start() 方法前调用 thread.setDaemon(true); 此线程设置为守护线程。
2、当主线程退出,守护线程自动退出。
3、gc() 垃圾回收就是一个典型的守护线程。
6)管程:(监视器)
二.:Lock 接口(5~7)
三.:线程间通信(8~11)
四.:线程间定制化通信(12~13)
五.:集合的线程安全(14~17)
六.:多线程锁(18~22)
七.:Callable 接口(23~25)
八.:JUC 强大的辅助类(26~28)
九.:可重入读写锁(ReentrantReadWriteLock) (29~32)
1:表锁 和 行锁:
1)表锁:修改时,整张表上锁。
2)行锁:只锁定修改的行。会发生死锁。(死锁:多个线程互相等待)
2:乐观锁 & 悲观锁:
1)悲观锁:能解决并发中的各种问题;不支持并发操作,效率低。
2)乐观锁:支持并发,效率高。(根据比对版本号是否相同,判断数据是否被修改过)
3:读写锁概述:(独占锁写 / 共享锁读)(互斥锁)
1)读锁:共享锁,可并发读。
2)写锁:独占锁,单独写。
3)但是:不能同时存在 读 和 写 线程。(读写互斥,读读共享)
4)缺点:
-1:读锁 & 写锁,都会产生死锁。
-2:容易造成 锁饥饿,一直读就没有写操作,
-3:读的时候不能写,写的时候不能读。(自己线程可以读)
4:读写锁案例:
1)集合读写操作方法:
class MyCache {
/**
* 创建 map 集合
* 数据 不断发生变化 使用 volatile
*/
public volatile static Map map = new HashMap();
/**
* 创建读写锁对象
*/
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
// 放数据
public void put(String key, Object value) {
// 添加写锁
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + ":正在写操作:" + key);
// 暂停一会
TimeUnit.SECONDS.sleep(2);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + ":写完了:" + key);
} catch (InterruptedException e) {
} finally {
// 释放写锁
rwLock.writeLock().unlock();
}
}
// 取数据
public Object get(String key) {
// 添加读锁
rwLock.readLock().lock();
Object result = null;
try {
System.out.println(Thread.currentThread().getName() + "--正在读取操作--:" + key);
// 暂停一会
TimeUnit.SECONDS.sleep(2);
result = map.get(key);
System.out.println(Thread.currentThread().getName() + "--读取完了--:" + key);
} catch (InterruptedException e) {
} finally {
// 释放读锁
rwLock.readLock().unlock();
}
return result;
}
}
2)测试 读写锁:
/**
* @author zhangxudong@chunyu.me
* @date 2022/4/14 12:48 下午
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 多线程放数据
for (int i = 0; i < 10; i++) {
// lamdb 表达式里的 变量,必须为 final 类型。
final int num = i;
new Thread(() -> {
myCache.put(num + "", num + "");
}, "ThreadName::" + i).start();
}
// 多线程取出数据
for (int i = 0; i < 10; i++) {
final int num = i;
new Thread(() -> {
myCache.get(num + "");
}, "ThreadName::" + i).start();
}
}
}
5)测试结果:(独占写,并发读)
ThreadName::0正在写操作:0
ThreadName::0写完了:0
ThreadName::1正在写操作:1
ThreadName::1写完了:1
ThreadName::2正在写操作:2
ThreadName::0--正在读取操作--:0
ThreadName::1--正在读取操作--:1
ThreadName::2--正在读取操作--:2
ThreadName::1--读取完了--:1
ThreadName::2--读取完了--:2
ThreadName::0--读取完了--:0
5:锁降级(学渣抄学霸作业)
1)概念:将写入锁 降级为 读锁。(目的:提高数据的可见性)
(读锁 不能升级为 写锁)
(写的权限,高于读的权限)
2)过程:(写锁 降级为 读锁)
3)代码演示:
public static void main(String[] args) {
// 创建可重入,读写锁对象
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
// 获取读锁
ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
// 获取写锁
ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
-- 锁降级过程 --
// 1、获取写锁
writeLock.lock();
System.out.println("获取到了写锁");
// 2、获取读锁
readLock.lock();
System.out.println("获取到了读锁");
// 3、释放写锁
writeLock.unlock();
// 4、释放读锁
readLock.unlock();
}
十.:阻塞队列(BlockingQueue)(33~35)
3:阻塞队列 用在哪里:
1)生产者 消费者模式:
-1:传统版:(1.0 -> 2.0 -> 3.0)
(while 不能改为 if,2个以上的多线程下,会控制不住)
(详见:https://blog.csdn.net/qq_43056248/article/details/109245417)
/**
* @author zhangxudong@chunyu.me
* @date 2022/4/16 3:31 下午
*/
public class MyBlockingQueue {
int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 加法
public void increment() {
lock.lock();
try {
// 1:判断
while (number != 0) {
// 等待,不能生产
condition.await();
}
// 2:干活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
// 3:通知唤醒
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// 减法
public void decrement() {
lock.lock();
try {
// 1:判断
while (number == 0) {
// 等待,不能生产
condition.await();
}
// 2:干活
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
// 3:通知唤醒
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
MyBlockingQueue myBlockingQueue = new MyBlockingQueue();
for (int i = 0; i < 100; i++) {
new Thread(() -> {
myBlockingQueue.increment();
}, "AAA 生产一个:").start();
}
for (int i = 0; i < 100; i++) {
new Thread(() -> {
myBlockingQueue.decrement();
}, "BBB 消费一个::").start();
}
System.out.println(myBlockingQueue.number);
}
}
-2:阻塞队列版:(3.0 版本)(高并发版本)
public class MyLock30 {
public static void main(String[] args) throws InterruptedException {
MyResource myResource = new MyResource(new ArrayBlockingQueue(3));
for (int i = 0; i < 2; i++) {
new Thread(() -> {
try {
myResource.myProduct();
} catch (Exception e) {
}
}, "product").start();
}
for (int i = 0; i < 2; i++) {
new Thread(() -> {
try {
myResource.myConsumer();
} catch (Exception e) {
}
}, "consumer").start();
}
TimeUnit.SECONDS.sleep(5);
myResource.stop();
}
}
class MyResource {
// 默认开启,进行生产 + 消费。(高并发)
private volatile boolean FLAG = true;
private AtomicInteger atomicInteger = new AtomicInteger();
private BlockingQueue<String> blockingQueue = null;
public MyResource(BlockingQueue blockingQueue) {
this.blockingQueue = blockingQueue;
}
public void myProduct() throws Exception {
String data = null;
boolean offer;
while (FLAG) {
data = atomicInteger.getAndIncrement() + "";
offer = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
if (offer) {
System.out.println(Thread.currentThread().getName() + ":生产队列成功。" + data);
} else {
System.out.println(Thread.currentThread().getName() + ":生产队列失败。" + data);
}
TimeUnit.SECONDS.sleep(1);
}
System.out.println(Thread.currentThread().getName() + ":生产者:停止插入队列:FLAG = false。");
}
public void myConsumer() throws Exception {
String result = null;
while (FLAG) {
result = blockingQueue.poll(2, TimeUnit.SECONDS);
if (result == null || result.equalsIgnoreCase("")) {
FLAG = false;
System.out.println(Thread.currentThread().getName() + ":消费队列接收数据:没有消息,停止" + result);
return;
}
System.out.println(Thread.currentThread().getName() + ":消费队列接收数据:" + result);
}
System.out.println(Thread.currentThread().getName() + ":消费者:停止接收数据:FLAG = false");
}
public void stop() {
FLAG = false;
}
}
2)线程池:
3)消息中间件:
十一.:线程池(ThreadPool)(36~40):
1:线程池 概述
1)线程池简介:
-1:一种线程使用模式。
-2:线程过多,会带来调度开销,进而影响( 缓存局部性 和 整体性能 )
-3:而线程池,维护着多个线程。等待着监督管理者,分配可并发执行的任务。
-4:这避免了,在处理短时间任务时,创建与销毁线程的代价,
-5:线程池不仅能够保证内核的充分利用,还能防止过分调度。
2)线程池 优势 & 特点:
-1:线程池做的工作主要是,控制运行的线程数量。
-2:处理过程中,将任务放入队列,然后在线程创建后启动这些任务。
-3:如果 线程数量超过了最大数量,超出数量的线程,排队等候,
-4:等其他线程执行完毕,再从队列中取出任务来执行。
3)它的主要特点为:
-1:降低资源消耗:减少 反复创建、销毁线程,所带来的消耗。
-2:提高响应速度:不需要等待创建线程的过程,可使用创建好的线程,立即执行。
-3:提高线程的可管理性:使用线程池,对线程进行统一的(分配、调优 和 监控)。
2:线程池 架构:
1)Java 中的线程池 通过 Executor 框架实现,该框架中用到了:下面这几个类
-1:Executor:
-2:Executors:
-3:ExecutorService:
-4:ThreadPoolExecutor:
2)架构图:
3:线程池 使用方式:(关闭资源很重要)
1)一池 N 线程:
public static void main(String[] args) {
// 5个线程的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
try {
// 同时处理 100 个请求。
for (int i = 0; i < 100; i++) {
executor.execute(() -> System.out.println(Thread.currentThread().getName() + ":处理业务:"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
2)一池一线程:(一个任务一个任务的执行)
Executors.newSingleThreadExecutor();
3)线程池根据需求创建线程:(可扩容,遇强则强)
Executors.newCachedThreadPool();
4:线程池 底层创建方式:
( LinkedBlockingQueue )
5:线程池 7 个参数:
1)创建线程池 底层方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
2)参数解读:
-1:corePoolSize: 线程池中,常驻的核心线程数。。【 创建好以后,一直存在,除非设置了allowCoreThreadTimeOut 】
-2:maximumPoolSize:线程池中,允许的同时执行的,最大线程数。
-3:keepAliveTime:多余的空闲线程,的存活时间。。【 当线程数大于核心数时,只要线程空闲时间,大于指定的 存活时间,就会释放空闲(核心线程之外的)的线程。】
-4:unit:keepAliveTime 参数的时间单位。
-5:workQueue:任务队列:存储被提交,但尚未被执行的任务。(本质是:阻塞队列)。。【 如果任务有很多,就会将 目前多的任务,放在队列里面。只要有线程空闲,就会去队列取出新的任务去执行。】
-6:threadFactory:创建 线程池中,工作线程的线程工厂。(一般默认即可)
-7:handler:拒绝策略。。如果队列满了,并且工作线程,等于允许最大线程数。。【按照 我们指定的 拒绝策略,拒绝执行任务】。
6:线程池 底层工作流程:
1)文字流程:
-1:线程池创建,准备好 核心数量的 核心线程,准备接受任务。
-2:core 满了,就将 再进来的 任务 放入到 阻塞队列中。空闲的 core 就会自己去 阻塞队列 获取任务执行。
-3:阻塞队列满了,就直接开新的线程,最大能开到 max 指定的数量。
-4:max 满了,就是用 RejectedExecutionHandler 拒绝策略来拒绝任务。
-5:max 都执行完成,有很多空闲线程,在指定时间, keepAliveTime 以后,释放 mac-core 这些线程。
2)问:一个线程池,core 7,max 20,queue 50 ,100并发进来,如何分配?:
-1:7 个会立即执行,50个进入队列,然后开辟 13 个新线程,剩余 30 个,使用拒绝策略进行拒绝,
-2:都执行完毕后,超过时间会关闭新开启的13 个临时线程。
3)工作流程图示:
4)JDK 内置的拒绝策略:(4种)
(队列满了 且 达到最大线程数量,使用拒绝策略)
(4 种拒绝策略,都实现了:RejectedExecutionHandler:接口)
7:自定义 线程池:
1)约定:
2)自定义线程池创建 :(工作中使用方式) (此定义参数:最多处理 8 个线程)
public static void main(String[] args) {
/**
* public ThreadPoolExecutor(int corePoolSize,
* int maximumPoolSize,
* long keepAliveTime,
* TimeUnit unit,
* BlockingQueue<Runnable> workQueue,
* ThreadFactory threadFactory,
* RejectedExecutionHandler handler) {
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
3,
1L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
8:生产环境,线程池参数配置:
1)工作中如何使用线程池:( new ThreadPoolExecutor(); )
2)如何自定义线程池参数:
// 查看 CPU 核数
System.out.println(Runtime.getRuntime().availableProcessors());
(核心线程数是 0 ,最大线程数计算如下:)
-1:CPU 密集型:
-2:IO 密集型:
十二.:分支合并框架(Fork / Join)(41~42):
1:Fork / Join 框架简介:
1)(Fork / Join)(分支 / 合并):它可以将一个大的任务,拆分成多个子任务,进行并行处理。
2)最后将子任务结果合并成最后的计算结果,并进行输出:
3)Fork / Join 框架要完成两件事情::
-1:Fork:把一个复杂的任务,进行拆分,大事化小。
-2:Join:把 拆分任务的结果进行合并。
4)示意图:
2:Fork 方法:
1)递归任务:继承后,可以实现递归调用的任务。
2)java 手册说明:
3)代码实现:(略)
十三.:异步回调(CompletableFuture)(43~43):
参照谷粒商城:https://www.yuque.com/aiyou-ywqry/xp148m/hgwlm7zp1vmdhkgp