多线程知识回顾总结

任务 线程 进程

本质上我们的大脑同一时间依旧只做了一件事。

一个进程Process可以有多个线程Thread

进程执行程序,一个java程序被执行时就有多个线程:主线程(用户线程)、gc线程(守护线程)等

并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发。
并行:同一个时刻,多个任务同时执行。多核cpu才可以实现并行。在多核cpu中并发并行也可以同时发生。

线程创建

继承Thread类(重点)

实现Runnable接口(重点)

实现Callable接口

死锁问题产生的四个条件:
互斥:一个资源每次只能被一个进程使用
阻塞不谦让,请求与保持
不能强行打破,不剥夺
循环等待条件

synchronized锁升级过程
对象的大小都为8bit的整数倍,倒数第三位为偏向锁标识,最后两个字节bit为锁的标志位
markword结构

无状态 启动四秒钟只有单线程访问 -> 偏向锁 将单线程id写入对象头,markword
当有少量并发发生时升级为 -> 轻量级锁 CAS,追求响应时间
当并发量增大响应时间3-5ms以上升级 -> 重量级锁,真正的锁,追求吞吐量
在这里插入图片描述

偏向锁时执行wait() hashcode()会直接进入重量级锁

不同类型的引用类型
在这里插入图片描述
在这里插入图片描述

强引用 常见 (可达性算法回收)
软引用 SoftReference 内存不足则回收适合做缓存
弱引用 WeakReference 如果没有任何强引用指向它,每次GC都回收
虚引用 PhantomReference 管理直接内存(堆外内存)回收时通知的作用

ThreadLocal内存泄漏问题

在这里插入图片描述
由于Thread中包含变量ThreadLocalMap,因此ThreadLocalMap与Thread的生命周期是一样长,如果都没有手动删除对应key,都会导致内存泄漏。

但是使用弱引用可以多一层保障:弱引用ThreadLocal不会内存泄漏,对应的value在下一次ThreadLocalMap调用set(),get(),remove()的时候会被清除。

因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。

ThreadLocal正确的使用方法
每次使用完ThreadLocal都调用它的remove()方法清除数据
将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。
在这里插入图片描述

ThreadLocal应用场景例:
spring 事务中控制 conn,保证拿到的时同一个conn

synchronized与lock区别?
① synchronized是关键字,lock是接口
② synchronized异常时会自动释放锁。lock必须使用unlock来解锁
③ synchronized不能中断,lock可以interrupt来主动中断
④ synchronized不知道自己是否拿到锁,lock可以通过trylock知道是否拿到锁
⑤ synchronized互斥锁,lock有不同的实现,readwritelock可以实现读写分离
⑥ synchronized使用wait、notify、notifyAll调度,lock使用Condition进行调度

哲学家餐桌问题解决:

① 将所有筷子放到一个集合,每次锁集合,小锁换大锁,类似去餐厅吃饭把餐厅锁住
② 混进一个左撇子(其中一个先不吃)
③ 混进多个左撇子(效率高:间隔一个先不吃)

两个线程依次循环输出问题解决:

方案一:
LockSupport.park();
LockSupport.unpark();
方案二:
synchronized wait() notify()
CountDownLatch(1)
countDownLatch.await();
countDownLatch.countDown();
方案三:
ReentrantLock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
c1.signal();//唤醒 必须包含在 lock和lock中间
c1.await();//挂起 必须包含在 lock和lock中间
方案四:
//TransferQueue中只能存放一个元素,当队列中没有元素时会阻塞,一直等待有元素到来再take取出。当队列中有元素时则不能再transfer放入元素,直到这个元素被取出
TransferQueue tq = new LinkedTransferQueue<>();
tq.take()
tq.transfer()

volatile使用场景

1 多个线程只有赋值操作,没有类似对自身++或–时,可以使用volatile替代synchronized。赋值操作时原子性的。
2 可以做刷新之前变量的触发器,当volatile标识的值改变后,其他线程一旦发现该变量修改的值后,触发获取到的该变量之前的操作都是最新的且可见的。
3 32位操作系统中,可以使long和double的赋值操作时原子性的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值