进程与线程
进程
- 指的是一个内存中运行的应用程序,每个进程都有一个独立内存空间
- 当一个程序一个线程都没有了的时候,程序结束
线程
- 是进程中的一个执行路径执行任务,线程之间可以互相切换,并发执行,一个进程最少有一个线程
- 同类的多个线程共享进程的堆和方法区的资源,每个线程有自己的程序计数器,虚拟机栈和本地方法栈,线程之间切换开销较小
- 线程是进程的进一步划分,一个进程在启动后有多个执行路径,里面的若干执行路径可划分为若干个线程
为什么程序计数器是线程私有的?
- 字节码解释器是通过程序技术器来按顺序执行指令,实现代码的流程控制-顺序执行,选择,循环,异常处理
- 多线程的情况下,程序计数器用于记录当前线程的执行位置,当本线程重新得到时间偏可以根据之前的记录继续执行程序
- 注意:执行的如果执行native方法,程序计数器记录的是undefined地址,只有执行Java代码的时候程序计数器才会记录下一条指令的地址
虚拟机栈&本地方法栈为私有
- 虚拟机栈:Java方法在执行的时候回创建一个栈帧用于储存局部变量表,操作数栈,常量池引用信息等。从方法调用至执行完成过程,对应着一个栈帧在Java虚拟机中入栈和出栈的过程
- 本地方法:与虚拟机栈作用类似,区别在于:虚拟机栈储存的是Java方法(字节码)执行时产生的信息,本地方法栈则为虚拟机使用到的native方法服务
结论:为了保证线程中的局部变量不被别的线程访问到,虚拟机和本地方法栈是线程私有
线程的调度
分时调度
- 平均分配每个线程使用CPU的时间,所有线程平均分配CPU的使用权
抢占式调度
- 优先级高的线程比低的线程占有CPU的概率高,如果优先级相同那么则会随机选择一个线程
- CPU切换线程也会消耗时间,一个CPU同时处理1000个进程,比1000个进程排序执行效率要低
- CPU的一个核心在同时只能执行一个线程,多个线程在该核心上来回切换看上去像是在同一时刻运行,多线程程序不能上程序执行的更快,但是可以提高程序运行的效率,提高CPU的使用率
线程的同步与异步
- 同步:排队执行,效率低但是安全
- 异步:多个线程所使用的资源可能会产生冲突,效率高
并发与并行
- 并发:同一时间段内,发生的一件或多件事情的场景。场景中往往有公用资源,容易针对这个公共资源产生瓶颈
- 并行:两个或多个事情同时正在发生,同时被处理,是真正的同时
实现Runnable
实现Runnable与继承Thread相比的优势:
- 通过创建任务然后将任务分配给线程的方式实现多线程,更加适合多个线程同时执行相同任务的情况
- 可以避免单继承所里带来的局限性
- 任务与线程分离,提高了程序的健壮性
- 线程池,接受Runnable,不接受Thread类型
守护线程
线程分守护线程和用户线程,当一个进程不包含任何的存活的用户线程时,进程结束。守护线程守护用户线程,当最后的用户线程结束时所有守护线程自动死亡
线程安全问题
- 隐式锁:同步代码块 + 同步方法,只要把关键语句和格式写好方法会自己锁自己解开
- 显示锁:自己锁,自己解开
隐式锁和显示锁的区别:
-
底层实现的不同
Sync:是Java中的关键字,由JVM来维护,是虚拟机层面上的锁
Lock:是一个具体的类,JDK5开始出现。通过Lock调用API来实现,是API层面的锁Sync是在底层通过monitorenter进行上锁,通过Monitor对象来完成,使用wait和notify方法这是依赖于monitor对象。只有在同步代码块和同步方法中才能调用wait和notify等方法,原因是只有在以上两种情况里JVM才会调用Monitor对象,通过monitorExit来退出锁。
Lock是通过调用对应的API方法来获取和释放锁的。
从编译后的汇编语句也可以看到Lock和Sync的区别。 -
使用方法的不同
Sync为隐式,Lock是显式。二者区别在于是否需要程序员动手去写相关代码来获取锁和释放锁。
Sync代码块执行完毕后,程序会自动释放所占用的锁,由系统维护,如果不是出了逻辑的问题不会出现死锁。
Lock需要程序员手动获取和释放,如果不释放锁可能会出现死锁:lock.lock( ), lock.unlock( )。配合try/finaly语句来完成 -
等待是否可以中断
Sync是不能中断的。除非抛出异常或者正常运行完毕
Lock是可以中断的,主要是一下两种方式:- 调用设置超时方法:tryLock(long timeout, timeUnit unit)
- 调用lockInterruptibly() 放在代码块中,调用interrupt()方法可以中断
线程的状态
new:线程刚刚被创建,尚未启动
Runnable:在Java虚拟机中执行的线程处于此状态
Blocked:被阻塞等待监视器锁定的线程处于此状态
Waiting:无限期等待另一个线程执行特定操作的线程
Timed Waiting:正在等待另一个线程执行最多指定等待时间的操作的线程
Terminated:线程死亡
线程在Running的过程中会遇到阻塞(Blocked)的集中情况
- 调用join()和sleep()方法,sleep()时间结束,join()中断,以上情况IO完成都会回到Runnable的状态,等待JVM的调度
- 调用wait(),将该线程放置到等待线程池(wait blocked
pool),直到被notify()/notifyAll(),线程被唤醒放置到锁定池(lock blocked pool),
释放同步锁线程回到Runnable - 直接对线程加入同步锁(Synchronized),将线程加入到(lock blocked pool),同步锁被释放进入Runnable
线程池
- 缓存线程池
长度无限,判断线程池是否有空闲线程,存在则使用,不存在创建线程放入线程池然后使用。 - 定长线程池
与缓存线程池相似,但是有固定长度。 - 单线程线程池
只有一个线程的线程池
引用链接
[1]: https://blog.csdn.net/ThinkWon/article/details/102021274
[2]: https://blog.csdn.net/kaizi_1992/article/details/105550689