进程与线程
进程 可以视为程序的一个实例,大部分程序可以运行多个实例进程(记事本、浏览器等)
线程 一个进程之内可分为一到多个线程
一个线程是一个指令流
进程是相互独立的,线程存在于进程内,是进程的一个子集
进程拥有共享的资源,如内存空间
进程间通信较为复杂
并行与并发
线程轮流使用CPU的做法被称为并发
多核CPU下,每个核都可以调度运行线程,这时候线程是并行的
应用
同步 需要等待结果返回才能继续运行
异步 不需要等待结果返回,就能继续运行
多线程可以让方法执行变为异步
多线程多核CPU才能提升效率,单核不行,因为微观下还是轮流执行的
IO操作不占用CPU,只是我们一般拷贝文件使用的是阻塞IO,这时线程虽然不用CPU,但需要一直等待IO结束
创建和运行线程
FutureTask能够接受Callable类型的参数,用来处理有返回结果的情况
每个线程启动,虚拟机就会为其分配一块栈内存
上下文切换
从使用CPU到不使用CPU称为一次上下文切换,上下文切换的原因
1.线程的CPU时间片用完
2.垃圾回收
3.有更高优先级的线程需要运行
4.线程自己调用了sleep、yield、wait、join、park、synchronized、lock等方法
频繁上下文切换会影响性能
常见方法
-
start()
启动一个线程,只是进入就绪状态,里面的代码要线程分到时间片时才能执行,每个线程对象的start方法只能调用一次,调用多次会出现异常 -
join()/join(long n)
等待某个线程运行结束
等待线程运行结束,最多等待n毫秒,如果等待时间大于实际等待时间,以实际时间为准,提前结束等待 -
isInterrupted()
判断是否被打断,若为true则会重置为false -
yield()
提示线程调度器让出当前线程对CPU的使用
调用yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其它线程 -
sleep()
sleep会让线程从Runing进入TimedWaiting(阻塞状态)
其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出异常
睡眠结束后的线程未必立刻得到执行 -
interrupt()
○1打断阻塞
阻塞状态打断后后抛出异常,抛出异常前会重置打断标记为false
○2打断正常
打断正常运行的线程,不会清空打断状态(True),线程依然会运行,可以根据打断标记写一段让它停止运行的代码。
○3打断park
打断park线程,不会清空打断状态,线程可以继续运行,一旦打断标记为true,再想让它停就不能停下来了
TimedWaiting不会分到时间片,而runnable会分到时间片,任务调度器在分配时间片时不会考虑阻塞状态的线程
主线程和守护线程
Thread.setDaemon(true) //将Thread线程设置为守护线程
只要有一个进程还在运行,则整个java进程都不会结束
守护线程,只要非守护线程全部结束了,即使守护线程没运行完,整个java进程也会强制结束