六、操作系统
1.说一说进程与线程?
进程是资源分配的基本单位,是程序执行的一个实例,于程序运行时创建;
线程是微进程,程序执行的最小单位,是进程的一个执行流,一个进程里包含多个线程;
协程是微线程,在子程序内部执行,可在子程序内部中断,转而执行别的子程序,在适当的时候再返回来接着执行。
2.进程、线程、协程的区别?
进程和线程区别:
1、进程是资源分配的最小单位,线程是程序执行的最小单位,也是处理器调度的基本单位,进程拥有自己的独立地址空间,每启动一个进程,系统就会分配地址空间,建立数据表来维护代码段,数据段,堆栈段,进程的全局变量是不共用的,这种开销是非常大,而线程是共享进程的数据,使用相同的地址空间,因此,CPU切换一个线程的开销远小于进程的切换;
2、线程之间的通信更加方便,同一进程下的线程共享全局变量、静态数据、而进程间的通信需要以通信的方式IPC进行,但是线程的缺点是同步和互斥是编写多线程的难点,多进程的优点是一个进程死掉不会对另外进程有影响,而多线程只要一个线程死掉整个进程就会死掉,每个线程拥有自己的栈段和寄存器组。
线程和协程的区别:
1、线程是程序执行的最小单位,协程在子程序内部执行;
2、协程执行效率极高:协程直接操作栈,基本没有内核切换的开销,所以上下文切换非常快,协程不需要多线程的锁机制,因为多个协程从属一个线程,不存在同时写冲突。
3.何时使用多进程?何时使用多线程?(考虑优缺点)
对资源保护和管理要求高,不限制开销和效率,使用多进程。
要求效率高,切换频繁,使用多线程。
4.创建进程的方式?
1、系统初始化,像后台进程,守护进程,
2、一个进程开启另一个进程fork(),
3、用户的交互式请求 。
子进程拷贝了父进程的数据段、堆、栈以及继承了父进程打开的文件描述符fd,但是父进程与子进程并不共享这些存储空间,这是子进程对父进程相应部分存储空间的完全复制,执行fork()之后,每个进程均可修改各自的栈数据以及堆段中的变量,而并不影响另一个进程,但是如果是vfork的话就会有影响。
5.进程有几种状态?
五种状态:创建、就绪、运行、阻塞、终止。
6.进程间通信方式有哪些?
管道、系统IPC(Inter-process Communication,包括消息队列、信号量、信号、共享内存)、套接字socket
1.管道(pipe)
2.信号量(semophore)
3.消息队列(message queue)
4.信号(signal)
5.共享内存(shared memory)
6.套接字(socket)
管道:分为有名和无名管道,无名管道只能有父子关系的进程间通信,是半双工通信,而有名管道允许无父子关系的进程间通信;
信号量:是个计数器,用来控制多个进程对共享资源的访问,用于进程间的同步和互斥;
消息队列:消息的链接表,放在内核中,消息队列独立于发送和接收的进程,进程终止后,消息队列并不会消失,消息队列可以实现消息的随机查询,可以按照消息的类型读取;
信号:用于通知接收进程某个事件已经发生。主要作为进程间以及同一进程的不同线程间的同步手段;
共享内存:共享内存就是映射一段能被其它进程访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问,共享内存是最快的IPC,它往往配合其它通信机制使用,如通过信号量来实现同步和通信;
原理:开辟一个物理内存空间,各个进程物理地址映射到自己的虚拟地址空间,通过虚拟地址就可以直接访问,进而实现数据共享,共享内存是最快的通信方式,因为少了数据的拷贝
步骤:
shmid = shmget(key, size, IPC_CREAT); // 1.开辟共享内存
addr = shmat(shmid, nullptr, 0); // 2.映射到进程虚拟地址
memcpy(addr, buf, sizeof(buf)); // 3.操作数据
int ret = shmdt(addr); // 解除映射
套接字:它可用于不同机器之间的进程通信
7.进程间通信的选择
管道通信主要是应用在进程间数据小、频率很高的消息
共享内存主要是在进程间共享数据大、读写频繁的数据(因为是把物理地址映射到进程)
其它考虑socket
8.什么是僵尸进程、孤儿进程、守护进程?
僵尸进程:一个进程使用fork出一个子进程,如果子进程退出,但是父进程没有使用wait()或者waitpid()函数回收子进程的资源,那么该进程就是僵尸进程;
孤儿进程:是指父进程异常退出,而子进程还没退出,那么子进程就会被1号进程(init)收养;
守护进程:是指后台进程,有意把父进程先结束,然后被1号进程收养。
9.僵尸进程有什么危害?
僵尸进程的进程号并不会被释放,但是系统的进程号是有限的,如果出现大量僵尸进程就会导致系统无进程号可用,无法产生新进程。
如何杀死僵尸进程:
可以通过杀死其父进程来结束僵尸进程。
10.线程间通信方法有哪些?
临界区、互斥量、信号量、事件、条件变量、读写锁
临界区:每个线程访问临界资源的那段代码叫临界区,每次只允许一个线程进入临界区,进入后其他线程无法进入;
互斥量:采用互斥对象机制,只有拥有互斥对象的线程才可以访问;
信号量:计数器,允许多个线程同时访问统一资源;
条件变量:通过条件变量通知操作的方式保持多线程同步;
读写锁:读写锁和互斥量类似,但互斥量要么是锁住状态,要么就是不加锁状态,而读写锁一次只允许一个线程写,但允许一次多个线程读,这样效率就比互斥锁要高。
11.说说线程,进程同步方式有哪些?
线程间的同步方式包括互斥锁、信号量、条件变量、读写锁。
12.什么是内核线程和用户线程?
用户线程是由用户进行管理,用户线程的创建、调度、同步和销毁全由库函数在用户空间完成,不需要内核的帮助,这种线程开销是比较小的;
内核线程由操作系统创建和销毁。
如何实现守护进程?
-
创建子进程,终止父进程
-
调用setsid创建一个新会话
-
将当前目录更改为根目录
-
重设文件权限掩码,文件权限掩码是指屏蔽掉文件权限的行为
-
关闭不再需要的文件描述符
13.进程调度算法即策略有哪些?
-
先来先服务
-
短作业优先调度
-
高优先级优先
-
时间片论
-
多级反馈队列
14.有抢占式和非抢占式的区别
非抢占式优先权算法 在这种方式下,系统一旦把处理机分配给就绪队列中优先权最高的进程后,该进程便一直执行下去,直至完成
抢占式优先权调度算法 在这种方式下,系统同样是把处理机分配给优先权最高的进程,使之执行。但在其执行期间,只要又出现了另一个其优先权更高的进程,进程调度程序就立即停止当前进程(原优先权最高的进程)的执行,重新将处理机分配给新到的优先权最高的进程
15.并发和并行
并发是对于单个cpu来说,是一个一个进程运行,但是线程的切换时间则是减少到纳秒数量级,多个任务可以不停的来回切换;
并行是对于多个CPU来说,多个进程同时运行;
区别:并行的同时是同一时刻可以多个任务在运行(处于running),并发的同时是不同线程快速切换
16.进程间的通信中的管道通信实现原理是什么:
操作系统在内核中开辟一块缓冲区(称为管道)用于通信
编程步骤:
-
父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端;
-
父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道;
-
父进程关闭管道读端,子进程关闭管道写端,父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INPUT 0 // 读数据fd
#define OUTPUT 1 // 发数据fd
int main() {
// 创建管道
int fd[2];
pipe(fd);
// 创建子进程
pid_t pid = fork();
if (pid < 0) {
printf("fork error!\n");
exit(-1);
}
else if (pid == 0) { // 执行子进程
printf("Child process is starting...\n");
// 子进程向父进程写数据,关闭管道的读端
close(fd[INPUT]);
write(fd[OUTPUT], "hello csdn!", strlen("hello csdn!"));
exit(0);
}
else { // 执行父进程
printf ("Parent process is starting......\n");
// 父进程从管道读取子进程写的数据,关闭管道的写端
close(fd[OUTPUT]);
char buf[255];
int output = read(fd[INPUT], buf, sizeof(buf));
printf("%d bytes of data from child process: %s\n", output, buf);
}
return 0;
}
17.说说什么死锁,产生的条件,如何解决?
死锁是指多个进程在执行过程中,因争夺资源而造成互相等待,此时系统产生了死锁。
产生条件:
-
互斥条件:进程对所分配的资源不允许其它进程访问,若其它进程需要访问,只能等待直到该进程使用完毕后释放资源;
-
请求保持条件:进程获得一定资源后,对其它资源发出请求,但该资源被其它进程占用,此时请求阻塞,而且这个进程不会释放自己已经占有的资源;
-
不可剥夺条件:进程获得资源,只能自己释放,不可剥夺;
-
环路等待条件:若干进程之间形成一种头尾相接等待资源关系。
解决:
资源一次性分配,从而解决请求保持的问题 ;
可剥夺资源:当进程新的资源未得到满足时,释放已有的资源;
资源有序分配:资源按序号递增,进程按照递增序号请求,释放则相反。
18.单核机械上写多线程程序,是否考虑加锁,为什么?
需要
因为线程锁主要是用来实现线程的同步和通信,在抢占式操作系统中,通常为每个线程分配一个时间片,当某个线程时间片耗尽时,操作系统会将其挂起,然后运行另一个线程。如果这两个线程共享某些数据,不使用线程锁的前提下,可能会导致共享数据修改而引起冲突。
19.互斥锁机制,互斥锁和读写的区别?
互斥锁机制:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入睡眠,等待锁释放时被唤醒。
互斥锁和读写锁:
(1) 读写锁区分读者和写者,而互斥 锁不区分
(2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象
函数 acquire() 获取锁,而函数 release() 释放锁
20.说说什么是信号量,有什么作用?
由于信号量只能进行两种操作等待和发送信号,即P(semWait)和V(semSignal),
(1)P操作:如果信号量sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行(信号量的值为正,进程获得该资源的使用权,进程将信号量减1,表示它使用了一个资源单位)。
(2)V操作:如果有其它进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1(若此时信号量的值为0,则进程进入挂起状态,直到信号量的值大于0,若进程被唤醒则返回至第一步)。
作用:用于多进程对共享数据对象的读取,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。
21.进程、线程的中断切换的过程是怎样的?
上下文切换指的是内核(操作系统的核心)在CPU上对进程或者线程进行切换
进程上下文切换
(1)保护被中断进程的处理器现场信息,
(2)修改被中断进程的进程控制块有关信息,如进程状态等,
(3)把被中断进程的进程控制块加入有关队列,
(4)选择下一个占有处理器运行的进程,
(5)根据被选中进程设置操作系统用到的地址转换和存储保护信息,
(6)根据被选中进程恢复处理器现场。
22.多线程和单线程有什么区别,多线程编程要注意什么,多线程加锁需要注意什么?
多线程和单线程的区别:
- 执行方式:单线程程序中的代码按照顺序一行一行地执行,而多线程程序则可以同时执行多个线程,每个线程可以执行程序的不同部分。
- 性能:多线程程序通常比单线程程序具有更高的性能,因为它可以同时处理多个任务。然而,这也取决于具体的硬件和操作系统环境,因为线程的管理和切换也可能带来额外的开销。
- 复杂性:多线程编程比单线程编程更复杂,因为需要处理线程间的同步和通信问题,以避免数据竞争和其他并发问题。
多线程编程要注意的事项:
- 线程安全:确保数据在多线程环境下是安全的,避免数据竞争和不一致。
- 线程间通信:设计好线程间的通信机制,确保线程能够正确地交换信息。
- 资源管理:合理分配和释放线程所使用的资源,避免资源泄漏和死锁等问题。
多线程加锁需要注意的事项:
- 粒度:锁的粒度应适中。过细的粒度可能导致过多的锁竞争,影响性能;过粗的粒度则可能限制并发度,无法充分利用多核处理器的优势。
- 避免死锁:设计加锁策略时要避免死锁,即两个或更多的线程无限期地等待一个资源,而该资源又被另一个线程持有并等待其他线程释放资源的情况。
- 锁的公平性:确保锁的分配是公平的,避免饥饿现象,即某个线程长时间得不到执行的机会。
- 减少锁的持有时间:尽量缩短锁的持有时间,以减少其他线程等待锁的时间,提高系统的并发性能。
- 考虑使用读写锁等高级锁机制:根据具体的访问模式,可以考虑使用读写锁、条件变量等高级锁机制来优化性能。
23.**为什么要创建线程池?**线程池的设计思路,线程池中线程的数量由什么确定?
原因:
创建和销毁线程的花销是比较大的,这些时间可能比处理业务时间还要长,这样频繁的创建和销毁线程,再加上业务工作的线程,消耗系统资源的时间,可能导致系统资源不足,线程池可以提升系统效率
实现线程池步骤:
-
设置一个生产者消费队列,作为临时资源,
-
初始化n个线程,并让其运行起来,加锁去队列里去任务运行,
-
当任务队列为空时,所有线程阻塞,
-
当生产者队列来了一个任务后,先对队列加锁,把任务挂到队列上,然后使用条件变量通知阻塞中的线程来处理。
线程的数量:
与CPU、IO/并发、并行有关
// 如果是CPU密集型应用,则线程池大小设置为:CPU数目 + 1
// 如果是IO密集型应用,则线程池大小设置为:2 * CPU数目 + 1
// 最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1) * CPU数目
所以线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程
24.说说select,epoll?
1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;而epoll保证了每个fd在整个过程中只会拷贝一次,
2)每次调用select都需要在内核遍历传递进来的所有fd,才知道哪些fd是被设置了;而epoll只需要轮询一次fd集合,同时查看就绪链表中有没有就绪的fd就可以了,select设置监听的集合不能被重新使用
3)select支持的文件描述符数量太小了,默认是1024;而epoll没有这个限制,它所支持的fd上限是最大可以打开文件的数目,这个数字一般远大于2048。
25.说说多路IO复用技术有哪些,区别是什么?
select,poll,epoll都是IO多路复用的机制,IO多路复用就是通过一种机制,可以监视多个文件描述符,一旦某个文件描述符就绪(一般是读就绪或者写就绪),能够通知应用程序进行相应的读写操作。
26.有名管道的通信:
-
创建fifo文件
-
打开fifo文件
-
读写fifo文件
27.信号的常见知识
1)信号的作用:用于通知接收进程有某种事件发生,除了用于进程间通信,还可以发送信号给自己
2)常见信号:
SIGKILL
SIGSTOP
SIGCONT
SIGALRM
3)发送信号:
1.int kill(pid_t pid, int sig)
参数说明:
如果pid为正,则将信号sig发送到pid指定ID的进程
如果pid等于0,则sig被发送到调用进程的进程组中的每个进程
如果pid等于-1,则sig被发送到调用进程有权发送信号的每个进程,进程除外(初始)
返回值:成功0,失败-1
发送信号给进程或线程
2.int raise(int sig) (只能发送给自己)
对于进程该函数相当于: kill(getpid(), sig);
对于线程该函数相当于:pthread_kill(pthread_self(), sig);
4)进程可以对信号的操作:
1.缺省方式
2.忽略信号
3.捕捉信号
5)创建定时器:
常用于检测网络超时
unsigned int alarm(unsigned int seconds);
参数说明:seconds为秒,如果seconds为0则取消定时器,
返回值:成功返回上个定时器的剩余时间,失败返回EOF,
注意一个进程只能创建一个定时器,时间到就产生SIGALRM信号,
通过这个函数我们可以设计一个定时器,时间到之后进程就会产生一个SIGALRM信号,接收进程只需调用void ( *signal(int signum, void (*handler)(int)) ) (int);函数收信号即可。
比如:
void signal_funtion(int signal)
{
if(signal == SIGALRM)
{
printf("alarm\n");
}
}
int main()
{
int ret;
ret=alarm(3); // 1.设置闹钟信号
printf("%d\n", ret);
signal(SIGALRM, signal_funtion); // 2.改变SIGALRM信号默认处理方式
ret = pause(); // 3.进入睡眠(防止闹钟时间过长,还没到时间程序就运行完),直到中断信号出现,才有可能继续往下
printf("pause %d\n", ret);
return 0;
}
28.共享内存
线程A
1)创建共享内存
2)映射到进程地址
3)通过映射地址操作共享内存
4)关闭映射
线程B
1)打开共享内存
2)映射
3)操作共享内存
4)关闭映射
29.说说软链接和硬链接的区别
软连接相当于快捷方式,硬连接是删除其中一个不会影响另一个
- 定义不同:
软连接又叫符号连接,它包含了对原文件的路径信息,可以看作是对原文件或目录的一个指针或快捷方式。如果原文件被移动、重命名或删除,软链接可能会失效(变成死链接)。
硬连接本质上是为一个文件取一个新的名称,它指向文件系统中的物理文件。原文件和硬链接在文件系统中实际上是同一个文件的两个不同名字。
- 限制不同
硬连接只能对已经存在的文件连接,不能交叉文件系统进行硬链接的创建,
软连接可以对不存在的文件或目录进行连接,可交叉文件系统
- 影响不同
删除一个硬链接文件并不影响其他有相同inode号的文件
删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接(即 dangling link,若被指向路径文件被重新创建,死链接可恢复为正常的软链接)。
30.简述Linux系统态与用户态,什么时候会进入系统态?
内核态是指拥有最高权限,可以访问所有的指令,用户态只能访问一部分指令
什么时候进入内核态:1.系统调用(主动);2.异常;3.设备中断
为什么区分用户态和内核态:在cpu中有些指令比较危险,如果用错就会导致系统崩溃,比如清内存
31.什么是页表,为什么要有?
页表是虚拟内存的概念,虚拟内存映到物理内存的映射表,就是页表
原因:虚拟内存要和物理地址映射起来,就必须要通过映射表找到物理地址,如果将每一个虚拟内存的 Byte 都对应到物理内存的地址,每个条目最少需要 8字节(32位虚拟地址->32位物理地址),在 4G 内存的情况下,就需要 32GB 的空间来存放对照表,那么这张表就大得真正的物理地址也放不下了,于是操作系统引入了页(Page)的概念,使用4个字节的映射就对应物理内存的1页—>4k内存,这样把全部虚拟地址映射到页表就会节省很大空间
一般来说进程的虚拟地址要想找到物理地址就必须通过MMU,MMU里面放的就是映射表,通过虚拟内存的高20位知道自己在映射表的位置,也就是对应的物理地址的页起始位置,虚拟地址的低12位对应物理地址的页里面的具体地址
比如:0x00003 005中前20位对应映射表的0x80003000,那么就会去找物理地址0x8000 3000为起始的页,005表示在页中的第几个,这样就可以找到具体物理地址
由于映射表是4个字节就映射物理地址的1页即4k,那么映射完整个物理地址之后就会节省大量空间,不同进程之间的页表是不一样的
32.简述操作系统中malloc的实现原理
malloc底层实现:
当开辟的空间小于 128K 时,调用 brk()函数;
当开辟的空间大于 128K 时,调用mmap()。
malloc采用的是内存池的管理方式,以减少内存碎片;先申请大块内存作为堆区,然后将堆区分为多个内存块。当用户申请内存时,直接从堆区分配一块合适的空闲快;采用隐式链表将所有空闲块,每一个空闲块记录了一个未分配的、连续的内存地址。
33.简述操作系统中的缺页中断。
malloc和mmap函数在内存分配时只是建立了进程的虚拟地址,并没有分配虚拟地址对应的物理内存,当进程访问这些没有建立映射关系的虚拟内存时,处理器自动触发一个缺页异常,引发缺页中断,缺页异常后将产生一个缺页中断,此时操作系统会根据页表中的外存地址在外存中找到所缺的一页,将其调入内存。
34.简述mmap的原理和使用场景
原理:mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read, write等系统调用函数;相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。
35.为什么使用虚拟内存
(1)扩大地址空间。每个进程独占一个4G空间,虽然真实物理内存没那么多;
(2)内存保护:防止不同进程对物理内存的争夺和践踏,可以对特定内存地址提供写保护,防止恶意篡改;
(3)可以实现内存共享,方便进程通信;
(4)可以避免内存碎片,虽然物理内存可能不连续,但映射到虚拟内存上可以连续。
36.中断的响应执行流程?听说过顶半部和底半部吗?
cpu接收中断——>保存中断上下文跳转到中断的处理函数——>执行中断上半部分——>执行中断的下半部分——>恢复中断的上下文
顶半部执行一般是比较紧急的任务,比如清中断,下半部执行的一些不太紧急的人任务可以节省中断处理时间
37.busybox是什么?
busybox缩小版的uinx系统常用命令工具箱(box)
38.什么是根文件系统
是内核启动时所挂载的第一个文件系统,内核代码映像文件保存在根文件系统中。
39.自旋锁是什么?信号量是什么?二者有什么区别?
自旋锁:——>不会进入睡眠
自旋锁只有两个状态锁定和解锁,在锁定期间其它进程是不能进行访问资源,比如B想访问只能在外面等到解锁之后才能访问,如果B等待的时间过长那么就要考虑使用互斥锁。
信号量:——>会进入睡眠
信号量是个计数器,用来统计资源的可用次数,比如B进程想使用资源,当资源可用的是时候就会去通知B,而不是让B在哪里等着,不会一直占用CPU,这样就可以提高系统的执行效率。
区别:
信号量会让等待信号的的进程进入睡眠,所以信号量适用于锁会被长时间持有的情况;
自旋锁会一直在哪里循环判断所是否可用占用极高CPU所以不适用长时间持有所的情况;
自旋锁禁止处理器抢占,信号量允许,这也就是为什么自旋锁不能睡眠的原因,如果自旋锁睡眠,那么就无法通过抢占唤醒睡眠的自旋锁;
信号量不能用于中断中,因为信号量会引起休眠,中断不能睡眠,自旋锁可以。
40.为什么堆的空间不是连续的?
堆内存的管理只要是靠链表来维护已用和空闲的内存,当我们申请堆内存空间时会在空间里面找到一个满足要求的内存,当我们释放大的内存空间时,这些大块空间就会和那些零散的空间合并成一个空间块。
41.线程的同步和互斥的区别和联系
同步是指按照一定顺序执行,同步里面包含了互斥,互斥是指一个资源只能出现一个进程进行访问,但是互斥没办法按照顺序执行,是无序的。
42.特殊进程
Linux下有3个特殊的进程,
idle进程(PID = 0),idle进程是由系统自动创建,运行在内核态,
init进程(PID = 1),init进程是由idle创建运行在用户空间,其父进程就是idle,
kthreadd(PID = 2),内核中线程,负责内核线程的创建工作,其父进程就是idle。
43.什么是虚拟内存
虚拟内存是一种内存管理技术,之所以有这个虚拟内存,主要是为了扩张内存,因为代码数据都是存在硬盘中的,而CPU是没办法直接去拿硬盘的数据,必须借助内存,但是这个内存是有限的,所以但内存不够的时候就会把一部分这个硬盘作为虚拟内存,这样cpu就可以读取虚拟内存的数据。
44.并发编程的三个概念
在并发编程中,我们通常会遇到以下三个问题:
原子性问题,可见性问题,有序性问题
原子性:即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行;
可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值;
有序性:即程序执行的顺序按照代码的先后顺序执行。
45. 同步和异步
同步一般是指阻塞等待,异步一般不需要等待,比如你发送数据到服务器,那么就不需要等待,直接去干别的事,一般来说异步的效率高于同步。