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