狂神说JUC学习笔记(二)
狂神说JUC的原版笔记:
链接:https://pan.baidu.com/s/12zrGI4JyZhmkQh0cqEO4BA
提取码:d65c我的笔记在狂神的笔记上增加了一些知识点或者做了些许补充/修改
如果狂神原版笔记的连接失效了请在评论区留言,我看到后会更新的
Callable
1、可以有返回值;
2、可以抛出异常;
3、方法不同,run()/call()
细节:
1、有缓存
2、结果可能需要等待,会阻塞!
常用的辅助类(必会)
CountDownLatch
输出结果(顺序不一定是一样的):
原理:
countDownLatch.countDown();
// 数量-1
countDownLatch.await();
// 等待计数器归零,然后再向下执行
每次有线程调用 countDown()
数量-1,假设计数器变为0,countDownLatch.await()
就会被唤醒,继续
执行!
CyclicBarrier
加法计数器
通过await等待,看线程是否达到7个!
输出结果(顺序不一定是一样的):
如果cyclicbarrier设置为8,那么达不到8个线程就无法“召唤神龙”成功。
Semaphore
Semaphore:信号量
例子:抢车位!
6车---3个停车位置
输出结果(顺序不一定是一样的):
原理:
semaphore.acquire()获得资源,如果资源已经使用完了,就等待资源释放后再进行使用!
semaphore.release()释放,会将当前的信号量释放+1,然后唤醒等待的线程!
作用: 多个共享资源互斥的使用! 并发限流,控制最大的线程数!
读写锁ReadWriteLock
补充
读写锁包含一对相关的锁,读锁用于只读操作,写锁用于写操作。读锁可能由多个读线程同时运行,写锁是唯一的。
1、读锁和写锁之间是互斥的,同一时间只能有一个在运行。但是可以有多个线程同时读取数据。
2、写入数据之前必须重新确认(ReCheck)状态,因为其他的线程可能会拿到写锁再一次修改我们已经修改过的值。这是因为前一个线程拿到写锁之后,后面的线程会被阻塞。当前一个线程释放写锁之后,被阻塞的线程会继续运行完成被阻塞的部分代码,所以才会出现这样的情况。
3、当某一个线程上了写锁之后,自己仍然可以上读锁,之后在释放写锁,这是一种降级(Downgrade)的处理方法。
读写锁(ReadWriteLock)包含如下两个方法:
1.读锁
Lock readLock()
2.写锁
Lock writeLock()
例子
先看看数据不可靠的例子
输出结果(顺序不一定是一样的):
可以看到上面的结果不是先写完在读取,而是有可能被其他线程插队的。所以如果我们不加锁的情况,多线程的读写会造成数据不可靠的问题。
我们也可以采用synchronized这种重量锁和轻量锁 lock去保证数据的可靠。
但是这次我们采用更细粒度的锁:ReadWriteLock 读写锁来保证
输出结果(顺序不一定是一样的):
总结
- 独占锁(写锁) 一次只能被一个线程占有
- 共享锁(读锁) 多个线程可以同时占有
- ReadWriteLock
- 读-读 可以共存!
- 读-写 不能共存!
- 写-写 不能共存!
阻塞队列
BlockQueue
是Collection的一个子类
什么情况下我们会使用阻塞队列?多线程并发处理、线程池
BlockingQueue 有四组api
方式 | 抛出异常 | 不会抛出异常,有返回值 | 阻塞,等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer(timenum.timeUnit) |
移出 | remove | poll | take | poll(timenum,timeUnit) |
判断队首元素 | element | peek | - | - |
同步队列
同步队列 没有容量,也可以视为容量为1的队列;
进去一个元素,必须等待取出来之后,才能再往里面放入一个元素;
put方法 和 take方法;
Synchronized 和 其他的BlockingQueue 不一样 它不存储元素;
put了一个元素,就必须从里面先take出来,否则不能再put进去值!
并且SynchronousQueue 的take是使用了lock锁保证线程安全的。
输出结果(顺序不一定是一样的):
线程池
线程池:三大方式、七大参数、四种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!我们需要去优化资源的使用 ===> 池化技术
线程池、JDBC的连接池、内存池、对象池 等等。。。。
资源的创建、销毁十分消耗资源
池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,以此来提高效率。
线程池的好处:
1、降低资源的消耗;
2、提高响应的速度;
3、方便管理;
线程复用、可以控制最大并发数、管理线程;
线程池:三大方法
- ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
- ExecutorService threadPool2 = Executors.newFixedThreadPool(5); //创建一个固定的线程池的大小
- ExecutorService threadPool3 = Executors.newCachedThreadPool(); //可伸缩的
七大参数
源码分析
狂神的银行排队例子
4种拒绝策略
-
new ThreadPoolExecutor.AbortPolicy(): //该拒绝策略为:银行满了,还有人进来,不处理这个人的,并抛出异常。超出最大承载,就会抛出异常:队列容量大小+maxPoolSize
-
new ThreadPoolExecutor.CallerRunsPolicy(): //该拒绝策略为:哪来的去哪里 main线程进行处理
-
new ThreadPoolExecutor.DiscardPolicy(): //该拒绝策略为:队列满了,丢掉异常,不会抛出异常。
-
new ThreadPoolExecutor.DiscardOldestPolicy(): //该拒绝策略为:队列满了,尝试去和最早的进程竞争,不会抛出异常
如何设置线程池的大小
1、CPU密集型:电脑的核数是几核就选择几;选择maximunPoolSize的大小
2、I/O密集型:
在程序中有15个大型任务,io十分占用资源;I/O密集型就是判断我们程序中十分耗I/O的线程数量,大约是最大I/O数的一倍到两倍之间。
回顾:手动创建一个线程池
__EOF__

本文链接:https://www.cnblogs.com/namelessguest/p/14092823.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!