* 1、JUC的概念
1.1什么事JUC
1.2.1:进行与线程
1.2.2:线程的基本状态
(1)NEW新建
(2)RUNNABLE准备就绪
(3)BLOCKED阻塞
(4)WAITING不见不散【过了时间一直等你】
(5)TIME_WAITING过时不候【公交过了时间就等不到那一辆了】
(6)TERMINATED 终结
1.2.3:wait/sleep的区别
1.2.4:并发和并行
并发:同一时刻一起抢一个东西
并行:一边一边【同一时刻多个东西】
1.2.5:管程
* 管程:保证在同一时间内只能有一个线程去访问被保护的数据或代码
* Monitor监视器
* 进入加锁,退出解锁,管程对象就是监视器
1.2.6:用户线程和守护线程
设置守护线程的参数要在start之前才能生效。
* 自己new的Thread,自定义线程就是【用户线程】【主线程结束了,用户线程还在运行,jvm】
* 守护线程【用在后台中】:比如说垃圾回收【没有用户线程了,都是守护线程,jvm结束】
* 2、Lock接口
2.1、复习Synchronized
2.1.1、Synchronized的作用范围:
方法锁、块级锁
2.1.2、多线程编程步骤上:
第一步:创建一个资源类,在资源类里面创建属性和操作方法【eg:空调】
第二步:创建多个线程,在多个线中调用资源类的操作方法
2.1.3、Synchronized实现买票的例子:
资源类:票
票数量
卖方法
线程:卖票员
卖票的方法【synchronized会自动加锁释放锁】
2.1.4:Lock接口【ReentrantLock、ReentrantReadLock、ReentrantWriteLock】
用Lock去写上面的卖票
* 3、线程间的通信【让线程按照咋们的要求去实行】
wait、notify/notifyAll
判断、干活、通知
效果:交替执行
多线程的编程过程【3步操作】
我要的效果是AA、BB、CC、DD
AA跟CC都是1
BB跟DD都是0
结果不满足条件。因为if判断存在虚假唤醒的问题。
例子:正常情况下飞机上发现有人带了可疑物品要下飞机然后进行检查物品,结果发现是恶作剧。
让乘客上飞机。上飞机的时候还要做一次安检。
如果不做安检就是虚假唤醒。
wait本来是释放锁的,如果别唤醒再参与竞争,然后执行操作。
虚假唤醒wait在原地被唤醒,然后操作。不用重新竞争了
解决方法,if判断 改成while就能解决这个问题
记住步骤
* 4、线程间定制化通信【想让线程怎么操作就怎么操作】
例子:启动三个线程,按照如下要求:
AA打印5次,BB打印10次,CC打印15次
AA打印5次,BB打印10次,CC打印15次
进行10轮
思想方法:
代码实现:
* 5、集合的线程安全
5.1集合线程不安全演示【平时我们很少用到线程,一般多是直接写代码】
以List为例
下面的添加方法没有锁,就会出现不安全问题
【1可能个数会少,2一边放一边取会有问题】
5.2解决方案Vector【古老的方案】
5.3解决方案Collections【collection的工具类】--->古老的方案
5.4解决方案CopyOnWriteArrayList--->juc里面提供的
CopyOnWriteArrayList的原理
写时复制技术【既保证了并发读,有保证了独立写】
读---->复制【写】---->合并
HashSet的不安全
HashMap的不安全
* 6、多线程锁
6.1:关于锁的8种情况
(1)、锁的是当前对象
(4)、两个不同对象用的是不同的锁
(5、6)static synchronized
(7、8)
6.2:公平锁非公平锁
默认非公平锁
非公平锁【可能会造成线程被饿死的情况,可能有人一张都卖不出去】
非公平锁:线程饿死
效率高
公平锁:【大家都能卖到票,只是多少问题】
阳光普照
效率相对较低
【公平锁礼貌问候,非公平锁,直接插队】
6.3:可重入锁也叫做递归锁
synchronized【隐式(自动)】和Lock【显式(手动)】都是可重入锁
(1)synchronized:同步代码块和同步方法做了演示
递归【循环递归调用】
(2)Lock【有上锁,有解锁,有上锁,有解锁......】
如果上了锁没有解锁,执行同一个操作用的同一把锁就会去等待前面解锁。这样容易死
6.4:
6.4.1:什么是死锁:
两个或两个以上进程,因为争夺资源造成的互相等待的现象。如果没有外力干涉,他们无法再执行下去
互相等待:
6.4.2:产生死锁的原因:(1)、资源不足
(2)、进程运行推进顺序不合适【A中要调B,B中要调A。AB都在被调用中】
(3)、资源分配不当
【编写一个死锁的代码(笑死很容易死锁耶)】
结果:jvm没有停止运行。
我想验证方法调用是否是死锁情况,如何验证?
6.4.3:
(1)jps类似Linux ps -ef【可以查看进程号】
(2)jstack + 进程号
jstack jvm自带的堆栈跟踪工具
* 7、CallAble接口【1、继承Thread类,重写run方法。2、实现Runable,重写run方法。3、实现CallAble,重写Call方法。4、通过线程池创建线程】
对比Runable和CallAble的区别,代码实现
调用的时候不能直接替换Runable
解决方案:
* 8、JUC强大的辅助功能
8.1:coutDownLatch
可能造成人没有,班长锁门了
解决方案:
8.2:CyclicBarrier
集齐7颗龙珠就可以召唤神龙
8.3:semaphore
6辆汽车,抢占3个车位进行停车
代码实现:
* 9、ReentrantReadWriteLock读写锁
9.1:乐观锁
9.2:悲观锁
表锁不会死锁。行锁容易死锁
9.3:读写锁
读锁:共享锁
写锁:独占锁
读写锁都容易死锁【注意死锁的概念】
读发生死锁:要等读完才能改,所以互相等待了,会死锁
写发生死锁
用map做缓存,不断放内容,取内容
改进:用读写锁【思考了以前的lock实现不了的还有synchronized。他们只是锁住单独的方法,还是会存在没洗完就获取的,所以用读写锁】
【读写锁,让写完了再读成功】
深入讲解读写锁:
下面没听懂他要干嘛
【懵逼】总结:rwx【read<write<execute】
写的时候可以读,读的时候不可以写。
读锁关闭了才能写。
* 10、Blockingqueue阻塞队列
队列和栈就是数据结构
阻塞队列分类:数组定长、链表定长等
代码演示:
第一组如果队列满了再添加或者是队列空了再删除会报错。记住队列是先进先出
第二组不报错,如果满了或是没有返回false
第三组:加或者是取,如果满了或是取空了就会处于阻塞状态
第四组,在第三组的基础上加了超时时间。阻塞超过多少时间自动结束程序
* 11、ThreadPool线程池
11.1:线程池概述
11.2:线程池的架构
11.3:线程池使用方式
Executors是Executor的工具类
Executors.newFixedThreadPool(int)
只创建了一个线程
Executors.newSingleThreadExecutor()
Executors.newCachThreadPool()
根据需求不断变化线程数量,最终可以充分处理不同的请求
代码演示:
一池n线程
结果:
一池一线程
可扩容的线程池【线程是不固定的,根据需求创建线程进行处理】
ThreadPoolExcutor的参数设置
11.6:线程池底层的工作流程
4中拒绝策略:
(1)超出最大线程数报异常(2)谁让你调用的就去找谁(3)把等待时间最长的抛弃(4)不跟你做任何处理,不对你的操作和命令做理睬
11.7:
参考阿里巴巴手册
自定义线程:【注意参数的含义】
* 12、Fork/Join分支合并框架
fork 【分支】、join【合并】
12.1:分支合并框架是什么
12.2:用这个框架来干什么?
算法:二分查找或者是二分拆分过程。
* 13、completableFuture异步回调
同步:找张三,他不在我坐在他位置上等他回来
异步:找张三,他不在,我告诉其他人我在找他。我会自己办公司干自己的事情了