- 进程与线程
- 多线程创建的4种方式
- 1、继承Thread类
- 2、实现Runnable接口(线程和任务分离)
- 接口中只有一个抽象方法的可以使用lambda表达式简化
- 方法1、2的本质区别,实际上还是执行的匿名内部类的run方法(源码)
- 小结:Thread:线程任务合并 Runable:线程任务分开(更容易与线程池结合,灵活) 【题外话组合优先于继承】
- 3、FutureTask(本身也是实现了Runnable接口)与Callable结合
- 主线程 task.get()方法会阻塞,直到线程执行完毕结果返回
- #线程的交替执行现象,顺序不受控制
- #几个查看进程、线程、cpu占用的方法
- 1、继承Thread类
- 栈和栈帧
- 栈内存是给线程使用的,每个线程有自己的栈内存,一个栈可以包含多个栈帧(一个栈帧对应一个方法的调用所占用的内存),每个线程只能有一个活动栈帧对应着正在执行的方法
- 什么时候会发生线程上下文切换(cpu不再执行当前线程,转而运行其他线程)
- 1、垃圾回收,工作线程暂停
- 2、cpu时间片用完
- 3、有更高优先级线程运行
- 4、线程自己调用sleep、yield、join等方法
- 当发生线程上下文切换时,须记住下一条jvm指令的执行地址,恢复运行时可以顺利返回->程序计数器
- 频繁切换线程,消耗性能,线程不是越多越好
- 线程常见方法
- start 启动一个线程,方法不可被多次调用
- run 线程运行
- sleep 使当前线程休眠 (运行[running]->阻塞[timed waiting]),睡眠结束后的线程需等cpu分配时间才能恢复运行
- 应用:防止while(true)空转,什么时候会用到while(true),服务端需要一直运行的程序
- yield 线程让步(运行->就绪[runnable]) 可能会被任务调度器再次分配时间片
- 线程优先级 设置了不代表优先(数字大),调度器可以忽略,只是说可能性
- join 等待某个线程结束之后运行,等待调用join的线程执行结束。sleep也可以替换join,但sleep多久时间不好把握,不建议
- join(time) 设置等待的时间
- 应用 同步、异步
- 从调用方角度看,需要等待结果返回即同步,否则异步
- 如果需要等待多个线程执行结束,那就多个线程都调用join
- interrupt(打断)关键字 [和interrued isinterrupt的区别,是否会清除打断标记]
- 打断阻塞(join、wait、sleep)线程,打断后抛异常,处于阻塞状态的线程被打断会清空打断标记,重置为假
- interrupt 打断正常运行线程,interrupt只是请求打断,具体是否中断线程运行要看是否对打断标记进行响应
- 一般终止线程的方式
- 错误思路
- 1、调用线程对象的stop方法,如果该线程锁住了共享资源,则其它线程无法永远无法获取锁
- 2、使用System.exit(int) 停止线程,会终止整个程序
- 过期方法(容易破坏同步代码块,造成线程死锁)
- stop() 停止线程运行
- suspend() 挂起线程
- resume() 恢复线程运行
- 过期方法(容易破坏同步代码块,造成线程死锁)
- 正确方式
- 两阶段终止模式(使某个线程“优雅”结束
- eg:在线程进行监控时,判断是否被打断(两种情况),被打断处理"后事",接着跳出循环
- 两阶段终止模式(使某个线程“优雅”结束
- 错误思路
- 守护线程
- eg:垃圾回收线程就是一个守护线程,默认情况下,Java进程会等待所有线程执行完毕,程序即结束但不会等待守护线程执行完毕
- 线程状态
- 操作系统角度分为5种,其中阻塞状态(如读写io时阻塞)如果不被唤醒,操作系统不会考虑调度它们
- 从Java api的角度则可分为6种,例子看代码很清晰 (黑马多线程)
- new 新建
- runnable (包含操作系统的可运行(就绪)和运行状态)
- waiting (阻塞-无限时的等待) 如join
- timed_waiting(阻塞-有限时的等待) 如sleep(time)
- blocked(阻塞-等待锁的释放) 如 synchronize
- terminated(死亡)
- 小结:线程创建方式、线程状态、两阶段终止
- 多线程创建的4种方式