[Java多线程]并发基础概念

并发基础

进程与线程

进程

  • 程序由指令和数据组成,指令的运行和数据的读写,必须将指令加载到CPU,数据加载到内存。在指令运行过程中还需要用到磁盘,网络等设备。进程就是用来加载指令,管理内存,管理IO的。
  • 进程是资源分配的最小单位

线程

  • 一个进程内可以分为一到多个线程。
  • 线程是CPU调度的基本单位。一个线程就是一个指令流,将指令流中的一条条指令以一定顺序交给CPU执行。

进程和线程的对比

  • 进程基本上相互独立,而线程存在于进程内,是进程的一个子集
  • 进程拥有共享的资源,如内存空间,供其内部的线程共享
  • 进程间通信较为复杂
    • 同一台计算机的进程通信称为IPC(Inter-process communication),todo 有哪些
    • 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,如HTTP
  • 线程通信相对简单,因为它们共享进程内的内存,多个线程可以访问同一个共享变量
  • 线程更轻量,线程上下文切换成本一般比进程上下文切换低

协程

一个进程可以产生许多线程,每个线程有自己的上下文,当我们在使用多线程的时候,如果存在长时间的 I/O 操作,线程会一直处于阻塞状态,这个时候会存在很多线程处于空闲状态,会造成线程资源的浪费。这就是协程适用的场景。

协程,其实就是在一个线程中,有一个总调度器对于多个任务,同时只有一个任务在执行,但是一旦该任务进入阻塞状态,就将该任务设置为挂起,运行其他任务,在运行完或者挂起其他任务的时候,再检查待运行或者挂起的任务的状态,使其继续执行

协程的方式更多用来做阻塞密集型(比如 I/O)的操作,计算密集型的还是使用线程更加合理。

Java 官方并没有协程库。但是Java社区提供了一个优秀的库 Quasar

并行与并发

一个CPU轮流做多件事就是并发。

多个CPU分别做多件事就是并行。

Linux 查看进程和线程状态

  • ps -fe 查看所有进程
  • ps -fT -p <PID>查看某个进程(PID)的所有线程
  • kill杀死进程
  • top按大写H切换是否显示线程
  • top -H -p <PID> 查看某个进程的所有线程

Java命令

  • jps查看所有Java进程
  • jstack <PID>查看某个Java进程(PID)的所有线程状态
  • jconsole查看某个Java进程中线程的运行情况(图形界面)

栈与栈帧(Frames)

  • 每个栈由多个栈帧(Frame)组成,对应这个每次方法调用锁占用的内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行得方法

如上图所示,程序进入main方法,生成main栈帧,main方法调用method1,生成method1栈帧,右侧局部变量表,分配了 x,y的内存,同理后面又调用了method2,生成了method2栈帧。当method2执行完,method2栈帧内存释放,以此类推,根据调用栈依次释放栈帧内存。
对应的内存分布图如下:

  • 局部变量是线程安全的,根据这个思路实现的线程安全技术叫线程封闭,即仅在单线程内访问数据

如数据库连接池,数据库连接池通过线程封闭技术,保证一个 Connection 一旦被一个线程获取之后,在这个线程关闭 Connection 之前的这段时间里,不会再分配给其他线程,从而保证了 Connection 不会有并发问题

线程上下文切换(Thread Context Switch)

CPU不再执行当前线程,转而执行另一个线程的代码:

  • 线程的CPU时间片用完了
  • 垃圾回收
  • 有更高优先级的线程需要执行
  • 线程自己调用了sleep,yield,wait,join,park,synchronized,lock等方法,主动放弃线程自己对CPU的使用权

当Context Switch发生时,需要操作系统来执行保存当前线程的状态,并恢复另一个线程的状态,JVM中对应的就是程序计数器(Program Counter Register),它的作用是记住下一条JVM指令的执行地址,是线程私有的

  • 线程的状态包含程序计数器,虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
  • Context Switch频繁发生会影响性能
原文地址:https://www.cnblogs.com/SimonZ/p/15669307.html