多线程
2024-08-06 22:09:53 3 举报
AI智能生成
5616514
作者其他创作
大纲/内容
JMM内存模型
共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
原子性
先读取,再写入,就一定有非原子性操作,导致线程不安全
可见性
有序性
共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入时,能对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
线程简介
实现多线程的几种方法
正确的理解
继承(extends)Thread
实现(implements)Runnable
优点
实现Runnable接口这种方法比较好,因为Java是单继承,多实现
常见面试题
启动线程的方法
start()
停止线程的方法
正确停止
Thread.interrupt
通知线程停止,而不是立即停止,等待此线程完成后进行停止。
Thread.isinterrupt
判断线程是否被通知停止
错误停止
Thread.stop
线程状态
新创建NEW
可运行Runnable
阻塞Blocked
等待Waiting
计时等待Timed Waiting
已终止Terminated
Thread和Object类重要方法
线程属性
线程优先级
10个级别,默认是5
线程异常
线程是把双刃剑
常见面试题
常用命令
阻塞
wait
阻塞线程
notify
唤醒等待线程
notifyall
唤醒所有等待线程
睡眠
sleep
睡眠线程
不会释放锁
中断
Join
来保证线程执行顺序
面试题
谦让
yield
释放当前线程时间片
可见性
Volatile
及时读取主内存的值
不能解决原子性问题
守护线程
线程类型默认继承自父线程
给用户线程提供服务
主线程执行完毕,守护线程就停止
临界区
临界区用来表示一种公共资源或者是共享数据,可以被多个线程使用。但是每一次,只能有一个线程可以使用它,一旦临界区资源被占用,其他线程想要使用这个资源就必须等待。
由于临界区的存在,多线程之间的并发必须受到控制,根据控制并发的册罗,我们可以把并发的级别进行分类,大致上可以分为阻塞,无饥饿,无障碍,无锁,无等待几种
由于临界区的存在,多线程之间的并发必须受到控制,根据控制并发的册罗,我们可以把并发的级别进行分类,大致上可以分为阻塞,无饥饿,无障碍,无锁,无等待几种
阻塞(Blocking)和非阻塞(Non-Blocking)
阻塞和非阻塞通常用来形容多线程间的相互影响。比如一个线程占用了临界区资源,那么其他所有需要这个资源的线程就必须在这个临界区中进行等待。等待会导致线程挂起,这种情况就是阻塞。此时如果占用资源的线程一直不愿意释放资源,那么其他所有阻塞这个临界区上的线程都不能工作。
线程池
锁的升级和降级
升级
不可以升级,升级会造成死锁
降级
可以进行降级
并发工具类分类
线程安全(底层原理)
线程安全
互斥同步
同步锁
synchronized
ReentrantLock
ReadWriteLock
同步工具类
Vector
非互斥同步
Atomic原子包
Atomic*基本类型原子类
Atomic*Array数组类型原子类
Atomic*Reference引用类型原子类
Atomic*Fieldupdate升级原子类
Adder加速器
LongAdder
DoubleAdder
Accumulator累加器
LongAccumulator
DoubleAccumulator
结合互斥和非互斥同步
线程安全并发容器
ConcurrentHashMap
CopyOnWriteArrayList
并发队列
阻塞队列
非阻塞队列
ConcurrentSkipListMap
ConcurrentSkipListSet
无同步方案,不可变
final关键字
线程封闭
ThreadLocal
栈封闭
线程安全(使用角度)
避免共享变量
线程封闭
ThreadLocal
两大使用场景
工具类
全局信息
每个ThreadLocal类中都有自己的实例副本,不共享
initialValue
初始化使用
get
get方法是先取出当前线程的ThreadLocalMap,然后调用Map.getEntry方法,把本Thread Local作为参数传入,取出对应的value
set
存入对象
remove
底层原理
栈封闭
共享变量,加以限制和处理
互斥同步
使用互斥锁
final关键字
使用成熟工具类
线程安全的并发容器
Atomic包,原子类
管理线程
线程池
Executor
Executors
ExecutorService
常见线程池
FixedThreadPool
CachedThreadPool
ScheduledThreadPool
SingleThreadExecutor
ForkJoinPool
总结
参数
能获取子线程的运行结果
Callable
Future
FutureTask
线程配合
CountDownLatch
CyclicBarrier
线程之间相互等待
线程会等待,直到足够多的线程到达事先规定的数目,一旦触发条件就可以进入下一步操作
Semaphore
信号量
可以通过许可证的数量来保证线程之间的配合
线程只有拿到许可证才能运行
Condition
可以等待线程被唤醒
Exanger
让两个线程在合适的时候交换对象
Phaser
计数可变
java中锁的分类
线程要不要锁住同步资源
锁住
悲观锁
如果我不锁住这个资源,别人就会来争抢,就会造成数据错误,所以每次悲观锁未来确保结果的正确性,会在每次获取并修改数据时,把数据锁住。
不锁住
乐观锁
多线程能否共享一把锁
可以
共享锁
读锁
可以一个线程或者多个线程进行读
读锁仅在等待队列列头节点不是想要获取写锁线程的时候可以插队
ReentrantReadWriteLock.ReadLock readLock
不可以
独占锁
写锁
但是只有一个线程可以写
写锁可以插队
ReentrantReadWriteLock.WriteLock
不能同时读或者同时写
多线程竞争时是否排队
排队
公平锁
有序的请求顺序
不排队
非公平锁
不完全按照请求的顺序
不排队可以避免程序的资源浪费,但是同样会造成饥饿
同一个线程是否可以重复获取同一把锁
可以
重入锁
如果当前线程获取到锁,可以再次获取锁,次数+1
不可以
不可重入锁
只能获取一次锁
是否可中断
可以
可中断锁
lock
不可以
非中断锁
synchronized
等待锁的过程
自旋
自旋锁
线程自旋后前面线程同步资源的锁已经释放,那么当前线程就可以直接获取同步资源不必阻塞
阻塞
非自旋锁
阻塞锁如果遇到线程没拿到锁的情况会直接把此线程进行阻塞,知道被唤醒
阻塞和唤醒一个Java线程需要操作系统切换CPU来完成这样会耗费处理器时间的
自旋锁的缺点:如果锁长时间被占用那么会白白浪费处理器资源
自旋锁实现的原理是CAS
千变万化的锁
互斥同步
synchronized
Lock接口
简介
Lock是一个功能接口
Lock接口最常见的实现是ReentrantLock
通常情况下,Lock只允许一个线程来访问共享资源,不过有些时候一些特殊的实现也可以允许并发访问比如,ReadWriteLock里面的ReadLock
Lock锁不会像Synchronized自动释放锁
Lock方法不能被中断,一旦陷入死锁,就会陷入永久等待
为什么使用Lock
子主题
方法介绍
Lock
获取锁,如果锁被其他线程获取则等待。
tryLock
获取锁,成功返回true,失败返回false
LockInterruptibly
如果获取不到锁,就等待
底层原理
调用lock方法,会先进行cas操作看下可否设置同步状态1成功,如果成功执行临界区代码
如果不成功获取同步状态,如果状态是0那么cas设置为1.
如果同步状态既不是0也不是自身线程持有会把当前线程构造成一个节点。
把当前线程节点CAS的方式放入队列中,行为上线程阻塞,内部自旋获取状态。
线程释放锁,唤醒队列第一个节点,参与竞争。重复上述。
非互斥同步
原子类
Atomic*基本类型原子类
AtomicInteger
Get
获取当前值
GetAndSet
获取当前值并修改新的值
GetAndIncrement
获取当前值自增
GetAndDecrement
获取当前值自减
AtomicLong
AtomicBoolean
Atomic*Array数组类型原子类
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
Atomic*reference引用类型原子类
AtomicReference
AtomicStampedRefeRence
AtomicMarkableRefeRence
Atomic*FieldUpdater升级类型原子类
AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater
Adder类加载器
LongAdder
DoubleAdder
Accumulator累加器
LongAccumulator
DoubleAccumulator
CAS原理
CAS
什么是CAS
比较并交换
比较并操作
CAS三个操作数
内存值
预期值
修改值
CAS底层
乐观锁思想
预期
如何实现原子操作
AtomicInteger加载Unsafe工具类,用来直接操作内存数据
用Unsafe来实现底层操作
用volatile来修饰Value字段保证可见性
GetandAdd方法分析
缺点
ABA问题
自旋时间较长
Final
final关键字
不可变性
线程安全
防止被修改
如果被final被修饰对象,对象引用对象不可变
构造方法不能被final修饰
final修饰方法不能被重写,和继承
JVM加载final
保存在常量池
并发容器
ConcurrentHashMap
线程安全HashMap
继承ReentrantLock
get方法解读
1.算出哈希值
2.判断当前是否初始化,如果没有则返回null
3.如果哈希值和key符合则返回value
4.如果哈希值是负数,会调用find查找红黑树节点
5.如果红黑树不是,则会while循环遍历链表查询,返回
put方法解读
1.判断Key是否为空,concurrentMap不能主键为空
2.计算出哈希值
3.初始化,如果已经初始化并且为空,就直接CAS放进去
1.成功返回
2.失败去判断扩容
4.进入链表存值操作
5.判断链表是否转成红黑树
满足红黑树条件
TREEIFY_THRESHOLD 满足8
红黑树占用空间比链表大两倍
正常情况下链表不会到达8
还要满足容量为64
哈希碰撞
先使用拉链法,如果达到添加则转成红黑树
1.7
采用分段锁
1.8
CAS+synchronization
复杂度
1.8
O(logn)
1.7
O(n)
只能保证单个操作线程安全
CopyOnWirteArrayList
线程安全的List
读很快
BlockingQueue
这是一个接口表示阻塞队列,适合数据共享通道
ConcurrentLinkedQueue
高效非阻塞队列
ConcurrentLinkList
线程安全的链表
synchronized
Hash Table
Vector
性能太低
Collections.synchronized
使用synchronized修饰方法快
集合
Map
HashMap
为什么线程不安全
如果两个Key同时put的话,计算出来的哈希值一样的话会丢失
扩容的时候也会丢失
Java7
在Java7的时候Hash Map扩容会给CPU造成100%
会造成链表的死循环
拉链法
数组加链表
如果Hash冲突会将冲突值加入链表
Java8
红黑树加链表
put
HashTable
SortedMap
TreeMap
LinkedHashMap
AQS
CAS
Unsafe
自旋
加锁解锁
0 条评论
下一页