先建maven项目,JDK8
学习方式:源码,官方文档
什么的JUC java util concurrent
就是这三个包
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
thread只是一个普通的线程类
runnable 没有返回值,效率比callable低,所以更多用callable
线程和进程 ,必须要用一句话总结出
自己:进程是资源分配的基本单位,程序获得CPU分配的资源后才能执行。线程是资源调度的管理者。
狂神:进程是一个程序,一个进程包含多个线程,Java默认有两个线程(main, GC)
55533.png" alt=“image-20220324161655533” style=“zoom:67%;” />
Java开启线程的方式:runnable ,thread,callable
但是Java无法真正的开启线程
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
init(group, target, name, stackSize);
}
public synchronized void start(){
group.add();
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//调用本地方法,所以说明Java没有开启线程的能力,调用底层的C++,Java无法控制硬件
private native void start0();
}
并发和并行的区别
并发:多线程操作同一个资源,单核CPU,进程切换,快速交替
并行:多个人一起走,多核CPU,多个线程可以同时运行,线程池
通过代码查看多核Runtime,输出12个
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
}
CPU密集型,IO密集型是什么
并发编程的本质:重复利用CPU资源
回顾多线程
线程有几个状态;
我:运行态、阻塞态、并行态、
狂:6个
public enum State {
/**线程新生
* Thread state for a thread which has not yet started.
*/
NEW,
/**运行
*A thread in the runnable state is executing in the Java virtual machine but it maybe waiting for other resources from the operating system such as processor.
*/
RUNNABLE,
/**
* A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or
*/
BLOCKED,
/**死死的等
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the following methods:
* is waiting for a specified thread to terminate.
*/
WAITING,
/** 超时等待,过期不候
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of the following methods with a specified positive waiting time:
*/
TIMED_WAITING,
/** 终止
* The thread has completed execution.
*/
TERMINATED;
}
wait方法和spleep区别
wait占用问题, 锁的问题
- wait来自Object类, sleep来自thread类(企业不会用,企业用 TimeUnit->归属于JUC的Concurrent)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ipuWfTKn-1648363882191)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220324164322519.png)]
- 锁的释放,wait释放锁,sleep抱着锁睡
- 使用范围不同,wait 必须在同步代码块中 sleep可以在任何地方使用,,,,,
- 是否需要捕获异常;wait会中断异常, sleep要捕获(写代码爆红)
Lock锁
Synchronized锁
狂神说有人写多线程方式是这样的,我是, 继承runnable方法,重写run,new一个新的线程
线程就是一个单独的资源类,没有任何的附属操作
实现接口就不是OOP了,耦合性极高
public static void main(String[] args){
new Thread;
}
class MyThread implements Runnable{
@Override
public void run(){
}
}
函数式接口, 匿名内部类, JDK1.8后简化为lambda表达式 (方法参数 )->{ 代码}
并发解耦
public static void main(String[] args){
Ticket ticket = new Ticket();
new Thread(()->{ticket.sale();}, "A").start();
new Thread(()->{ticket.sale();}, "B").start();
new Thread(()->{ticket.sale();}, "C").start();
}
多个线程用一个资源会发生争抢的现象。
解决:加Sychronized锁,避免争抢。
lock是接口,有3个实现类,可重入锁,读锁,写锁
公平锁:先来后到,
非公平锁:可以插队,防止饥饿,默认
public ReentrantLock() {
sync = new NonfairSync();
}
如何使用:try业务代码, catch, fanally解锁
Lock三部曲:
- new ReentantLock();
- 代码块加锁 Lock.lock();
- finally解锁
labmda表达式写锁
Synchronized 和Lock的区别 重要
- Synchronized是内置的java关键字, Lock是一个Java类
- Synchronized无法判断获取锁的状态, Lock可以判断是否获取到了锁
- Synchronized会自动释放锁, Lock手动释放。如果不释放,死锁。
- Synchronized线程不会争抢,线程1不释放,线程2 就一直等待, Lock不会一直等待。
- Synchronized可重入锁,不可以中断,非公平, Lock可重入,可判断,默认非公平的,但是可以自己设置。
- Synchronized适合锁少量的代码同步问题, Lock适合所大量的同步代码。
所以锁是什么,如何判断锁的是谁?
传统的生产者消费者问题,防止虚假唤醒
Synchronized 的 wait notify版本
-
线程中的通信问题:生产者消费者,线程交替执行操作同一个变量,即通知和等待唤醒
等待,业务逻辑,通知
public static void main(String[] args) { Data data = new Data(); //不要显示创建线程池,用线程池创建线程 //https://blog.csdn.net/luxing205/article/details/114639313 new Thread(()->{for(int i = 0; i < 10; i++){ try { data.decrement(); }catch (InterruptedException e){ e.printStackTrace(); } } }, "B").start(); } public class Data{ private int number = 0; public synchronized void increment(){ //if判断只会判断一次,所以有两个线程进来的时候会发生问题 if (number != 0){ //等待 this.wait(); } number++; //通知其他线程 this.notifyAll(); } public synchronized void decrement(){ if (number == 0){ } number--; } }
面试基本问题:单例模式,排序算法,生产者消费者,死锁
上述代码4个线程还安全吗?
防止虚假唤醒问题,改为while判断
//if判断只会判断一次,所以有两个线程进来的时候会发生问题 while (number != 0){ //等待 this.wait(); }
JUC版的LOCK 生产者消费者问题
Synchronized: wait, notify
Lock: await, signal
通过Lock找到Condition,lock接管了synchronized, condition取代了对象监视器方法的使用
public static class Data{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
//业务:判断->执行->通知
while (number != 0){
condition.await();
condition.signalAll();
}
number++;
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
任何一个新的技术,绝对不是仅仅覆盖原来的技术,一定有优势。
Condition可以精准的唤醒和通知线程
Condition实现精准的唤醒和通知线程
同步监视器:
精准唤醒:
8锁现象 就是关于锁的8个问题
学习方法:费曼学习法,学习兴趣,学习态度
什么是锁,锁的是谁
锁是new出来的, 而对象是由Class模板new出来
public static void main(String[] args) {
Phone phone = new Phone();
//不是由于先调用,而且先调用不代表先执行,而是锁的作用
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"B").start();
}
public static class Phone{
public synchronized void sendSms(){
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
}
由于这两个方法用的phone这个同一把锁,谁先拿到谁先执行。
加上TimeUnit.SECONDS.sleep(4)延迟和hello方法不加锁可以影响打印顺序
没有锁就不是同步方法,不受锁的影响。
synchronized锁的对象是方法的调用者
public static void main(String[] args) {
Phone phone = new Phone();
//不是由于先调用,而且先调用不代表先执行,而是锁的作用
new Thread(()->{
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.hello();
},"B").start();
}
static class Phone{
public synchronized void sendSms(){
try {
//是这里的休眠影响了时间,所以在这里要搞清锁的是谁
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
public void hello(){
System.out.println("hello");
}
}
增加两个静态的同步方法
静态方法与普通方法的区别:先执行静态方法(static类一加载就有了) + synchronized
先执行它
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("sendSms");
}
小结:
new this 具体的一个对象
class 具体的模板
Q:既然不占用CPU为什么占用内存,难道是在后台偷偷运行?
读取速度:
CPU -> 内存 -> SSD -> 磁盘 -> 网络
纳秒 -> 微秒 -> 毫秒 -> 毫秒 -> 秒
那哪些软件会主动存到磁盘?为什么选择存在磁盘?
集合类不安全
CopyOnWriteArrayList 写入时复制
COW计算机程序设计领域的一种优化策略。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vPLsTtXS-1648363882195)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220324224118807.png)]
单线程的代码永远是安全的
List多线程下不安全:
//随机字符串
list.add(UUID.randomUUID().toString().subString(0,5));
如何解决?即并发安全
- List list = new Vector<>(),不好 list并发下不安全
- Arrays -> Collections.synchronzedList(new ArrayList<>());
- List list = new CopyOnWriteArrayList<>();
一定要自己动手!
读CopyOnWriteArrayList源码
list.foreach里面有哪些参数,四大函数式接口
CopyOnWriteArraySet HashSet
BlockingQueue: 阻塞队列
不安全set 初始:又出现ConcurrentModificationException
解决措施:
- 工具类写法:Set set = Collections.synchronizedSet(new HashSet<>());
- JUC写法:Set set = new CopyOnWriteArraySet<>();
HashSet的底层:就是hashmap
key是无法重复的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hH0efzx7-1648363882197)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220324225656156.png)]
IO类,集合类,常用类
ConcurrentHashMap
HashMap也是不安全的 并发异常
map是这样用的吗,默认等价于什么?
Map<String, String> map = new HashMap<>();
加载因子loadFactor=0.75,初始化容量initialCapacity why???
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wp5nVtBP-1648363882198)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220324230006824.png)]
看HashMap源码!!!
有问题:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W4xixEhb-1648363882199)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220324231022517.png)]
synchronized
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8d228Kw6-1648363882200)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220324231221044.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8W5Wx2sa-1648363882200)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220324231358310.png)]
解决:
Map<String, String> map = new ConcurrentHashMap<>();
研究ConcurrentHashMap原理!
Callable 可以返回结果和抛出异常,类似于similar
@FunctionalInterface
public interface cllable<V>{
V call() throws Exception;
}
public static class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return 1024;
}
}
不要用runnalbe,因为他没有返回值
implements Callable
//new Thread(new Runnable()).start();
//new Thread(new FutureTask<V>()).start();
//new Thread(new FutureTask<V>(Callable)).start();
应用:
new Thread.start();
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread); // implements Callable<V>
new Thread(futureTask, "A").start();
Integer o = (Integer)futureTask.get();
阿里巴巴规范
分析:
JDK线程池框架继承关系:
线程池构造类ThreadPoolExecutor实现方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) { ... ... }
JDK自带的工具类可以创建的线程池种类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gjLKDfKe-1648363882201)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220325095237983.png)]
引java.util的包 https://juejin.cn/post/6844904074668670984
三大常用辅助类,高并发
CountDownLatch 计数器
减法计数器
两个方法:
CountDownLatch.countDown() 数量-1
CountDownLatch.await 等待计数器归零再向下执行, 防止有些数据未操作完
每次有线程调用CountDownLatch()数量-1 ,假设计数器变为0, CountDownLatch.await()就会被唤醒,继续执行。
CyclicBarrier 加法,等待达到数量才会执行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gwOf4toj-1648363882203)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220325110743674.png)]
<=7代表永远达不到8,就不会开启新的线程
Semaphore ,信号量 并发
有个acquire(),假设资源已经满了就等待释放, release方法,信号量释放+1,等待唤醒线程
计数信号量。 从概念上讲,信号量维护一组许可。 如有必要,每个 acquire() 都会阻塞,直到获得许可,然后再接受它。 每个 release() 添加一个许可,可能会释放一个阻塞的获取者。 但是,没有使用实际的许可对象; Semaphore 只是对可用数量进行计数并采取相应措施。
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.
抢车位:6个车3个位置,轮流等待车位,用于限流,控制线程最大数,保证高可用。
并发思想大多与操作系统有关
ReadwriteLock 读写锁是ReEntrantLock的更加细粒度
ReentrantReadLock读锁独占 reentrantWriteLock 写锁共享,多个线程同时占有
只有一个接口 reentrantlock 都可以被多线程读,写只能被一个线程写
volatile 保证原子性
private vlatile map<String, Object> map = new HashMap<>();
map.put写
map.get读
阻塞队列 队列FIFO
ElasticSearch
不得不阻塞:Input读,队列满了,阻塞等待,队列空了,等待生产,阻塞等待
ArrayBlockingQueue, LinkedBlockingQueue
java中的list和set是什么,父:Collection ->Queue -> BlockingQueue,Abstract(非阻塞),Deque ->Array,Linked, , 双端队列是一种具有队列和栈性质的抽象数据类型。 双端队列中的元素可以从两端弹出,插入和删除操作限定在队列的两邊进行。
什么情况需要用到阻塞队列:
多线程,线程池的挂起等待,多线程并发处理,线程池
BlockingQueue的四组API
- 抛出异常
- 不会抛出异常
- 阻塞等待
- 超时等待
方式 | 抛出异常 | 不会抛出异常 | 阻塞等待 | 超时退出 |
---|---|---|---|---|
添加 | add | offer | put | offer(timeout) |
移除 | remove | poll空参 | take | poll(timeout) |
检测队首元素 | element | peek |
//队列的大小一定要写
ArrayBlockingQueue b = new ArrayBlockingQueue<>(3);
2.不抛出异常,返回布尔值
SychronousQueue同步队列
隶属于BlockingQueue,不存储元素,没有容量,必须等待取出后才能放,即容量为1
new两个线程,一个负责
线程池 三大方法、七大参数、4种拒绝策略
池化技术 事先准备好一些资源,有需要就给
程序的运行本质就是利用系统资源去完成任务,所以需要优化CPU资源的实验
线程池、JDBC连接池、内存池、对象池 需要设置核心参数
线程池的好处:降低资源的消耗、提高响应速度、方便管理
即 线程复用、控制最大并发数、管理线程
Executors
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8JFfJ5g-1648363882203)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220325171243391.png)]
三大方法:Single:单一线程, Fixed:固定的数量,Cached:容量可伸缩
七大参数
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
阿里巴巴规范
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hruPWlcK-1648363882204)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220325173322451.png)]
手动创建线程池
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hvviqhgm-1648363882204)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220325175547980.png)]
DiscardOldestPolicy队列满了,尝试去和最早的竞争,不会抛出异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LwyIk8lG-1648363882205)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220325181458914.png)]
CPU密集型和IO密集型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-te2Pk8DV-1648363882205)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20220325204358011.png)]
-
}throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler;
阿里巴巴规范
[外链图片转存中...(img-hruPWlcK-1648363882204)]
##### 手动创建线程池
[外链图片转存中...(img-Hvviqhgm-1648363882204)]
==DiscardOldestPolicy队列满了,尝试去和最早的竞争,不会抛出异常==
[外链图片转存中...(img-LwyIk8lG-1648363882205)]
#### CPU密集型和IO密集型
[外链图片转存中...(img-te2Pk8DV-1648363882205)]