进程
程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存,在指令运行过程中还需要用到磁盘、网络等设备,进程就是用来加载指令、管理内存、管理IO等操作。
当一个程序被运行,从磁盘加载这个程序的代码至内存,这是就开启了一个进程。
进程就可以视为程序的一个实例,大部分程序可以同时运行多个实例进程(例如记事本等),也有的程序只能启动一个实例进程(例如网易云音乐等)。
线程
一个进程之内包含一到多个线程。
一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行。
Java中,线程作为最小调度单位,进程作为资源分配的最小单位。
二者对比
进程基本上相互独立,而线程存在于进程中,是进程的一个子集。
进程拥有共享的资源,如内存空间等,供其内部的线程共享。
进程间通信较为复杂
1.同一台计算机的进程通信成为IPC(Inter-process communication)
2.不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如HTTP
线程通信相对简单,因为他们共享进程的内存,例如多个线程可以访问同一个共享变量。
线程更轻量,线程上下文切换成本一般要比进程上下文切换低。
并行与并发
单核CPU下,多个线程实际还是串行执行,操作系统中有一个组件叫任务调度器,将CPU的时间片(windows下时间片最小约15毫秒)分给不同的程序使用,只是由于CPU在线程间(时间片很短)的切换非常快,人类感觉是同时运行的,总结为一句话就是:微观串行,宏观并行。
一般会将这种线程轮流使用CPU的做法成为并发(concurrent)。
CPU | 时间片1 | 时间片2 | 时间片3 | 时间片4 |
---|---|---|---|---|
core | 线程1 | 线程2 | 线程3 | 线程4 |
表1
图1
多核CPU下,每个核(core)都可以调度运行线程,这时候线程可以是并行(parallel)。
CPU | 时间片1 | 时间片2 | 时间片3 | 时间片4 |
---|---|---|---|---|
core1 | 线程1 | 线程1 | 线程3 | 线程3 |
core2 | 线程2 | 线程4 | 线程2 | 线程4 |
表2
图2
引用Rob Pike的一段描述:
并发(concurrent)是同一时间应对(dealing with)多件事情的能力。
并行(parallel)是同一时间动手做(doing)多件事情的能力。
创建和运行线程
方法一、直接使用Thread
//创建线程对象 Thread thread = new Thread(){ @Override public void run() { //TODO doing 线程要执行的任务 } }; //启动线程 thread.start();
方法二、使用Runnable配合Thread
把【线程】和【任务】分开
Thread代表线程
Runnable可运行的任务(线程要执行的代码)
Runnable runnable = new Runnable() {
@Override
public void run() {
//TODO doing 线程要执行的任务
}
};
//创建线程对象
Thread thread = new Thread(runnable);
//启动线程
thread.start();
方法三、FutureTask配合Thread
FutureTask能够接受Callable类型的参数,用来处理有返回结果的情况
FutureTask<Object> task = new FutureTask<>(new Callable<Object>() {
@Override
public Object call() throws Exception {
//TODO doing 线程要执行的任务,并返回任务结束的结果
return null;
}
});
//创建线程对象
Thread thread = new Thread(task);
//启动线程
thread.start();
try {
//当前线程会阻塞等待子线程任务task返回结果
Object result = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
作者:刘跃明
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)