基本概念
进程和线程
进程
应用程序其实是由指令和数据组成,而指令需要加载至cpu中执行,数据需要加载至内存,在指令执行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理IO的。
进程可以视为一个应用程序的实例,大部分的程序可以同时运行多个实例,比如电脑中的记事本。
线程
程序有非常多个,但是cpu是有限的,也就有了所谓的CPU调度,线程是cpu调度的最小单位。线程必须依赖于进程存在,线程是进程的一个实体,是cpu调度和分配的最小单位
进程的通信方式
同一台计算机中的进程通信叫IPC ((Inter-process communication)),不同计算机中的进程通信叫RPC。
进程间的通信方式有:
- 管道,又分为匿名管道和命名管道,匿名管道可用于父子进程间通信,命名管道除了拥有匿名管道的功能外还允许不是父子进程间的通信
- 信号,用于通知进程有某事件发生,个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。
- 消息队列,消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点。具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。
- 共享内存,最有用的进程间通信方式,多个进程可以访问同一块内存区域,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。
- 信号量(semaphore),主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。
- 套接字(socket):这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。
CPU核心数与线程数
cpu的核心数决定了能够并行运行多少线程,一个CPU核心只能运行一个线程。
但 Intel引入超线程技术后,产生了逻辑处理器的概念,使核心数与线程数形成1:2的关系。
在Java中提供了Runtime.getRuntime().availableProcessors()
,可以让我们获取当前的CPU核心数,注意这个核心数指的是逻辑处理器数。
上下文切换
cpu调度中我们知道cpu是频繁切换不同的线程执行的,线程在使用cpu执行时会使用cpu的寄存器与程序计数器的,如果出现了线程切换,那么cpu就会把当前线程在寄存器与程序计数器中的上下文数据拷贝到内存中去,然后再执行另一个的线程,此时会从内存中把另一个线程的上下文在cpu寄存器中进行恢复。
上下文切换通常是计算密集型的,因为涉及一系列数据在各种寄存器、 缓存中的来回拷贝。就CPU时间而言,一次上下文切换大概需要5000~20000个时钟周期,相对一个简单指令几个乃至十几个左右的执行时钟周期。
并行与并发
并行就是同一个时间执行多个任务,并发就是cpu在我们察觉不到的速度去交替执行多个任务。
当谈论并发的时候一定要加个单位时间,也就是说单位时间内并发量是多少?离开了单位时间其实是没有意义的。
两者区别:并发是交替执行,并行是同时执行,
Java线程的启动和终止
Java把操作系统中的线程封装成了Thread类,当我们调用thread.start()方法后才会真正意义上创建一个线程,否则它就是一个普普通通的java对象。