首页 > 编程语言 >java多线程:详解JUC

java多线程:详解JUC

时间:2023-02-22 19:46:51浏览次数:42  
标签:JUC java synchronized lock 死锁 线程 内存 多线程

对应狂神说JUC视频
1.JUC是什么
java.util下的几个包的简称 涉及到多线程的开发
java.util.concurrent
java.util.atomic
java.util.concurrent.locks


2.
线程和进程
进程:多个程序的集合
线程:进程中的一个执行命令
一个进程往往有多个线程
java默认是有2个线程?main GC
Thread Runnable Callable
java真的可以开启线程嘛?开不了 (实际调用底层的native方法,用c++去操作硬件)

并发 并行
并发 多线程操作同一个资源
并行 多个人一起执行
CPU单核 模拟出多条线程 快速交替
CPU多核 多个线程同时执行 线程池
并发实际上轮流处理多个任务 并行是同时处理多个任务

并发编程的本质:充分利用CPU的资源

3.线程有几个状态
NEW 新生
RUNNABLE 就绪
BLOCKED 阻塞
WAITING 等待
TIMED_WAITING 超时等待
TERMINATERD 终止

wait/sleep区别
1.wait-》Object sleep ->Thread
2.关于锁的释放:wait会释放锁 sleep不会
3.使用范围不同:wait必须在同步代码块中 sleep可以在任何地方执行
4.是否需要捕获异常:sleep是必须捕获异常,wait也需要的,notify 和notifyAll不需要

线程常用方法
sleep 当前线程阻塞的毫秒数 可以模拟网络延时 倒计时
join athread.join() 当前线程等待a线程终止 a线程插队
yield this.yield 让出当前cpu 谁执行谁礼让
停止线程 设置标志位

 

死锁 某一个同步块同时拥有“两个以上对象的锁”

4.锁
synchronized
new Thread(()->{}).start();
//lambda表达式的方式实现多线程 -》用匿名内部类实现Runnable接口,再用线程对象开启线程
// 函数式接口方便用lambda表达式构造出实例

5.Lock
reentrantLock 可重入锁 reentrantReadWriteLock.readlock reentrantReadWriteLock.writeLock
公平锁 先来后到
非公平锁 可以插队 (默认)
1)new ReentrantLock();
2)Lock.lock();//加锁
3)finally-> lock.unlock();//解锁


6. lock和synchronized 区别
1)synchronized 内置的java关键字,lock是一个java类
2)synchronized 无法判断获取锁的状态 lock可以判断
3)synchronized 会自动释放锁,lock需要手动加锁 手动释放锁,可能会遇到死锁
4)synchronized 线程1(获得锁-》阻塞)线程2(等待);lock会有一个trylock尝试获取锁,不会造成长久的等待
5)synchronized 是可重入的,不可以中断的,非公平锁,lock,可重入的,可以判断锁,可以自己设置公平锁和非公平锁
6)synchronized 适合锁少量的代码同步问题,lock适合锁大量的同步代码

可重入锁(递归锁)
在外层使用锁之后,内层依然可以使用,并不发生死锁
拿到了外面的锁 就可以自动获取里面的锁
一个类的A B两个方法 A B 都有同一个锁,A方法调用,获得锁,在A方法的锁还没有被释放,B方法也获得该锁


7.生产者和消费者问题
1).管程法 : 设置缓冲区 生产者将生产好的数据放入缓冲区 消费者从缓冲区拿出数据
2).信号灯法:设置一个标志位 控制生产 消费
synchronized wait notifyAll
四个线程 出现虚假唤醒问题 if改为while
lock版
Lock condition.await();condition.signalAll();
condition 精准的通知和唤醒线程

10.8锁现象
(1.先发短信
(2.先发短信
synchronized 锁的对象是方法调用 两个方法拿到的同一个锁,谁先拿到谁执行
(3.加入一个普通方法
先hello
不受synchronized锁的影响,不用等待锁的释放
(4.两个方法拿的不是一个对象
先打电话
两个对象两把锁,不会出现等待
(5.synchronized方法变成静态方法
始终先发短信
锁的是class类模板
(7.一个静态同步 有一个非静态同步
先打电话
不是一个对象


11.集合
1)CopyOnWriteArrayList 线程安全 底层是使用Lock锁
写入时复制 读的时候没有锁写的时候加锁
2)set不安全
使用Collections工具类的synchronized包装的Set类
Set<String> set = Collections.synchronizedSet(new HashSet<>());
使用CopyOnWriteArraySet 写入复制的JUC解决方案
hashSet底层就是一个hashMap
3)Map不安全
hashMap 并发修改异常
ConcrrentHashMap

Callable
可以有返回值
可以抛出异常
方法不同 run()/call()

get() 方法获取返回值 可能会产生阻塞
结果有缓存,同一个方法再次调用只会打印一次

常用的辅助类
1)CountDownLatch 减法计数器 一次性
2)CyclickBarrier 加法计数器
3)semaphore 信号量

 

18.读写锁 ReentrantReadWriteLock
写的时候只有一个线程写,读的时候可以多个线程一起读
独占锁(写锁)共享锁(读锁)


19阻塞队列
队列先进先出
写入:如果队列满了不得不阻塞 取:如果队列是空的,必须阻塞
什么情况下会使用阻塞队列:多线程并发处理 线程池


20BlockingQueue 有四组api
方式 抛出异常 不会抛出异常,有返回值 阻塞,等待 超时等待
添加 add offer put offer(timenum.timeUnit)
移出 remove poll take poll(timenum,timeUnit)
判断队首元素 element peek - -

21同步队列
SynchronousQueue put一个元素,必须take 才能put


22线程池
三大方法 7大参数 4种拒绝策略
池化技术:事先准备好一些资源,如果有人要用,就来我这里拿,用完之后还给我,提高效率
好处:
1)降低资源的消耗 (不用每次自己创建线程)
2)提高响应速度 (不用等线程创建就能立即执行)
3)方便管理 (线程池可以统一分配 调优 监控)
线程复用 可以控制最大并发数 管理线程
方法:
Executors.newSingleThreadExecutor();//单个线程
Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
Executors.newCachedThreadPool();//可伸缩的


线程池不允许用Executors去创建 而是通过ThreadPoolExecutor的方式
原因:FixedThreadPool SingleThreadPool 允许队列长度太长,可能会堆积大量请求
CachedThreadPool 允许的创建线程数量太大

public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程池大小
long keepAliveTime, // 超时了没有人调用就会释放
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue,// 阻塞队列
ThreadFactory threadFactory,// 线程工厂:创建线程的,一般不用动
RejectedExecutionHandler handler// 拒绝策略
)

超时等待 超时没人调用 最大线程池线程会释放

拒绝策略
AbortPolicy 满了 不处理新数据,抛出异常
CallerRunsPolicy 由调用线程处理
DiscardPolicy 丢弃任务,不抛出异常
DiscardOldestPolicy 队列满了,尝试和最早的竞争,不会抛出异常


24如何设置线程池大小
CPU密集 获取CPU的核数
IO密集型 程序中 有多少个大型任务 设置为1~2倍


25-26函数式接口
传统技术必会:泛型 枚举 反射
lambda表达式 函数式接口 stream流式计算 链式编程
链式编程 @Builder
可读性高 代码更简洁
不利于调试
跳过


27Stream流式计算
集合 mysql本质就是用来存储东西的
计算应该交给流来操作


28 ForkJoin
特点:工作窃取 先执行完毕的进程会把没执行完的进程窃取过来执行
双端队列
ForkJoinPool 可以将任务分成多个子任务 放进线程池中 再合并结果
steam流
long sum = LongStream.range(0L, 20_0000_0000L).parallel().reduce(0, Long::sum); //用一个并行流计算


异步回调
Future 对未来的某个事件结果进行建模
CompletableFuture
(1)没有返回值的runAsync异步回调
(2)有返回值的supplyAsync异步回调

30.JMM
java内存模型
volatile 是java虚拟机提供的轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排


JMM :不是一个存在的东西,而是一个关于同步的约定:
作用:保证线程的安全
1)线程解锁前,必须把共享变量刷回主存
2)线程加锁前,必须读取主存中的最新值到工作内存中
3)加锁和解锁是同一个锁


8种操作
read load use assign(赋值) write store
lock unlock

制定规则:
不允许read load store和write操作之一单独出现, 用了read必须load,使用了store必须write
不允许线程丢弃最近的assign操作,即工作变量的数据变了,必须告知主存
不允许一个线程将没有assign的数据从工作内存同步回主内存
一个变量同一时间只有一个线程能对其lock
如果对一个变量进行lock操作,会清空所有工作内存中此变量的值
如果一个变量没有被lock,就不能对其进行unlock

原子性:一组操作,要么全部执行,要么全部不执行
可见性:只要一个线程对共享变量的值做了修改,其他线程都将马上收到通知,立即获得最新值
有序性:对于单线程的代码,我们总是认为程序是按照代码的顺序进行执行,多线程下,就可能发生指令重排


31.volatile 可见性及非原子性验证
volatile 不保证原子性
lock synchronized 保证原子性

使用原子类 解决原子性
number.incrementAndGet(); //底层是CAS保证的原子性
在内存中修改值

32.禁止指令重排
volatile 会加一道内存屏障 这个内存屏障可以保证在这个屏障中的指令顺序
禁止上面指令和下面指令顺序交换

33.DCL懒汉式
饿汉式 浪费空间
懒汉式
多线程并发 创建了多个对象
双重检测锁:如果多个线程同时通过了第一次检查,其中一个线程首先通过了第二次检查并new对象,
其他线程不会再去实例化对象

实例化对象不是原子性操作,怎么解决?
加volatile 防止指令重排

利用静态内部类 创建唯一对象
可以用反射破坏
枚举可以防止反射破坏
原因:反射在通过newInstance创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败

CAS
原子类保证原子性 利用的底层的CAS 操作
native https://blog.csdn.net/wike163/article/details/6635321
一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
Unsafe类 java通过这个类操作内存

CAS compareAndSet 比较并交换
incrementAndGet 当前内存地址中的值 当前对象的内存地址偏移值 相同,当前内存地址值+1
比较当前工作内存中的值和主内存的值 如果这个值是期望的 那么则执行操作 如果不是就一直循环 使用的是自旋锁
缺点:循环会耗时
一次性只能保证一个共享变量的原子性
它会存在ABA问题


CAS ABA问题
在CAS操作时,其他线程将变量值A改成了B,然后又改回了A。
等到本线程使用期望值A与当前变量进行比较时。发现变量A没有变,于是CAS就将A值进行了交换操作

原子引用:带版本号的原子操作
解决ABA问题:带版本号的原子操作
对应思想:乐观锁(只是在执行更新的时候判断一下在此期间别人是否修改了)


36.可重入锁(递归锁)
拿到了外面的锁 就可以自动获取里面的锁
1)lock锁必须配对,相当于lock和 unlock 必须数量相同;
2)在外面加的锁,也可以在里面解锁;在里面加的锁,在外面也可以解锁;
Java中ReentrantLock和synchronized都是可重入锁

37.自旋锁
getIntVolatile
拿不到锁的线程一直循环等待


38.死锁
线程1持有资源A,线程2持有资源B,同时想申请对方的资源,互相等待造成死锁
死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用
2.请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源不释放
3.不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

怎么判断死锁
jps -l 查看进程号
jstack 进程号 -》查看堆栈信息 找到死锁问题


 

标签:JUC,java,synchronized,lock,死锁,线程,内存,多线程
From: https://www.cnblogs.com/yxj808/p/17145617.html

相关文章

  • Java ”框架 = 注解 + 反射 + 设计模式“ 之 注解详解
    Java”框架=注解+反射+设计模式“之注解详解每博一文案刹那间我真想令时光停住,好让我回顾自己,回顾失去的年华,缅怀哪个穿一身短小的连衣裙和瘦窄的短衫的小......
  • java中定义一个类可以同时继承两个类
    转载:百度知道(baidu.com)java中一个类不能直接继承两个类。比如说这样:classAextendsB,C不能这样写,因为java不支持多继承,但是可以像下面这样实现继承多个类:classAext......
  • Java里的对象是咋回事
    前言在上一篇文章中,壹哥给大家介绍了Java中的类及其特点、创建过程等内容,相信你现在已经知道该如何创建一个Java类了。接下来在本篇文章中,壹哥会继续带大家学习面向对象中......
  • Java的多线程+Socket
    客户端: packagecom.wulala;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net......
  • 解决java.lang.NoClassDefFoundError:Could not initialize class net.sf.cglib.beans
    异常描述项目引入Alibaba的EasyExcel工具,编译没有报错,在请求导出Excel文件的接口时,log异常出现以下错误java.lang.NoClassDefFoundError:Couldnotinitializeclassnet.......
  • Java常用类之Object源码分析
    一、概述理论上Object类是所有类的父类,即直接或间接的继承java.lang.Object类。由于所有的类都继承在Object类,因此省略了extendsObject关键字。Object类属于java.lang包......
  • Java 调接口类似postman用form-data方式post传输数据
    转自JavaHttpClient发送multipartform-data的Post请求  publicstaticStringgateway(Stringparam,Stringparamvalue,Stringservicename,Stringinterface_id......
  • 如何通过Java 代码设置 Word 文档页边距
    页边距是指页面的边线到文字的距离。通常可在页边距内部的可打印区域中插入文字和图形,也可以将某些项目放置在页边距区域中(如页眉、页脚和页码等)。在我们用的Word文档中,都......
  • Java培训班学什么班型比较好
    了解互联网的同学应该都知道Java软件开发是比较热门的行业,学习的人也是络绎不绝,市面上的相关培训急剧增多,随着不同机构的成立,学习方式也不在是单一化的,在各式各样的教学方......
  • Java培训班出来能找到工作吗,能否直接就业!
    Java已经是世界上非常流行的编程语言,至今已经牢牢抓住了开发市场,现在的Java开发人才的需求量逐年增加,相对来说是很好找工作的,至于“Java培训班出来能找到工作吗”的问题,还......