笔记10:多线程
线程简介
线程、进程、多线程
- 线程就是独立的执行路径
- 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程
- main() 称之为主线程,为系统的入口,用于执行整个程序
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如cpu调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程创建
三种创建方式:
- Thread class 继承Thread类(重点)
- Runnable接口 实现Runnable接口(重点)
- Callable接口 实现Callable接口(了解)
继承Thread类
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
1 | package com.wang.demo01; |
线程开启不一定立即执行,由cpu调度执行
网图下载:
1 | package com.wang.demo01; |
实现Runnable接口
1 | package com.wang.demo01; |
小结
继承Thread类:
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
实现Runnable接口:
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
并发问题
1 | package com.wang.demo01; |
案例:龟兔赛跑-Race
- 首先来个赛道距离,然后离终点越来越近
- 判断比赛是否结束
- 打印出胜利者
- 龟兔赛跑开始
- 故事中乌龟是赢的,兔子需要睡觉,所以我们来模拟兔子睡觉
- 终于,乌龟赢得比赛
1 | package com.wang.demo01; |
实现Callable接口(了解即可)
Callable的好处:
- 可以定义返回值
- 可以抛出异常
1 | package com.wang.demo02; |
Lambda表达式
1 | package com.wang.lambda; |
1 | package com.wang.lambda; |
静态代理模式
- 你:真实角色
- 婚庆公司:代理你,帮你处理结婚的事
- 结婚:实现都实现结婚接口即可
演示:实现静态代理对比Thread
1 | package com.wang; |
线程状态
线程方法
线程停止
- 不推荐使用JDK提供的stop()、destory()方法。【已废弃】
- 推荐线程自己停止下来
- 建议使用一个标志位进行终止变量;当flag=false,则终止线程运行
1 | package com.wang.state; |
线程休眠
- sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延迟,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
1 | package com.wang.state; |
1 | package com.wang.state; |
线程礼让_yield
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功!看CPU心情
1 | package com.wang.state; |
线程强制执行_join
- Join合并线程,待线程执行完成后,再执行其他线程,其他线程阻塞
- 可以想象成插队
1 | package com.wang.state; |
观测线程状态
1 | package com.wang.state; |
线程优先级
- 优先级的设定建议在start()调度前
- 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了。这都是看CPU的调度
1 | package com.wang.state; |
守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录操作日志,监控内存,垃圾回收等待…
1 | package com.wang.state; |
线程同步
线程同步机制
-
线程同步:多个线程操作同一个资源
-
并发:同一个对象被多个线程同时操作
-
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
-
线程同步形成条件:队列+锁
三大不安全案例
- 买票
1 | package com.wang.syn; |
- 银行取钱
1 | package com.wang.syn; |
- 集合
1 | package com.wang.syn; |
同步方法及同步块
同步方法
同步方法弊端:
代码分为只读的A代码+可修改的B代码
- 方法里面需要修改的内容才需要锁,锁的太多,浪费资源
同步块
上述三大不安全案例加锁变安全:
1 | package com.wang.syn; |
1 | package com.wang.syn; |
1 | package com.wang.syn; |
测试JUC安全类型的集合
1 | package com.wang.syn; |
死锁
1 | package com.wang.thread; |
- 死锁避免方法:
Lock(锁)
ReentrantLock:可重入锁
1 | package com.wang.gaoji; |
synchronized与Lock的对比:
生产者消费者问题
线程通信
线程通信-分析
解决方式
方式1
1 | package com.wang.gaoji; |
方式2
- 并发协作模型"生产者/消费者模式"—>信号灯法
1 | package com.wang.gaoji; |
使用线程池
1 | package com.wang.gaoji; |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 王同学の小屋!
评论