加油学习,奥里给!!!
数据存在数据库中时,是离散的存储,数据表中相邻的字段存储位置并不相邻,因此在顺序查找字段时需要一个一个查找,这是很大的io消耗。因此需要降低io引入数据结构,二叉树,红黑树,b树,哈希表等。
数据存在数据库中时,是离散的存储,数据表中相邻的字段存储位置并不相邻,因此在顺序查找字段时需要一个一个查找,这是很大的io消耗。因此需要降低io引入数据结构,二叉树,红黑树,b树,哈希表等。
并发编程篇
从0开始深入理解并发、线程与等待通知机制
- 大厂面试题:进程间的通信
同一台计算机的进程通信称为 IPC(Inter-process communication),不同计
算机之间的进程通信被称为 R(mote)PC,需要通过网络,并遵守共同的协议,比
如大家熟悉的 Dubbo 就是一个 RPC 框架,而 Http 协议也经常用在 RPC 上,比如
SpringCloud 微服务。- 大厂常见的面试题就是,进程间通信有几种方式?
- 管道,分为匿名管道(pipe)及命名管道(named pipe):匿名管道可用
于具有亲缘关系的父子进程间的通信,命名管道除了具有管道所具有的功能外,
它还允许无亲缘关系进程间的通信。- 信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较
复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器
收到一个中断请求效果上可以说是一致的。- 消息队列(message queue):消息队列是消息的链接表,它克服了上两
种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息
队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。- 共享内存(shared memory):可以说这是最有用的进程间通信方式。它
使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共
享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。- 信号量(semaphore):主要作为进程之间及同一种进程的不同线程之间
得同步和互斥手段。- 套接字(socket):这是一种更为一般得进程间通信机制,它可用于网络
中不同机器之间的进程间通信,应用非常广泛。同一机器中的进程还可以使用
Unix domain socket(比如同一机器中 MySQL 中的控制台 mysql shell 和 MySQL 服
务程序的连接),这种方式不需要经过网络协议栈,不需要打包拆包、计算校验
和、维护序号和应答等,比纯粹基于网络的进程间通信肯定效率更高。
CPU 核心数和线程数的关系
- 前面说过,目前主流 CPU 都是多核的,线程是 CPU 调度的最小单位。同一
时刻,一个 CPU 核心只能运行一个线程,也就是 CPU 内核和同时运行的线程数
是 1:1 的关系,也就是说 8 核 CPU 同时可以执行 8 个线程的代码。但 Intel 引入
超线程技术后,产生了逻辑处理器的概念,使核心数与线程数形成 1:2 的关系。
在我们前面的 Windows 任务管理器贴图就能看出来,内核数是 6 而逻辑处理器
数是 12。
在 Java 中提供了 Runtime.getRuntime().availableProcessors(),可以让我们获
取当前的 CPU 核心数,注意这个核心数指的是逻辑处理器数。
获得当前的 CPU 核心数在并发编程中很重要,并发编程下的性能优化往往
和 CPU 核心数密切相关。- 上下文切换(Context switch)
既然操作系统要在多个进程(线程)之间进行调度,而每个线程在使用 CPU
时总是要使用 CPU 中的资源,比如 CPU 寄存器和程序计数器。这就意味着,操
作系统要保证线程在调度前后的正常执行,所以,操作系统中就有上下文切换的
概念,它是指 CPU(中央处理单元)从一个进程或线程到另一个进程或线程的切换。
上下文是 CPU 寄存器和程序计数器在任何时间点的内容。
寄存器是 CPU 内部的一小部分非常快的内存(相对于 CPU 内部的缓存和 CPU
外部较慢的 RAM 主内存),它通过提供对常用值的快速访问来加快计算机程序的
执行。
程序计数器是一种专门的寄存器,它指示 CPU 在其指令序列中的位置,并
保存着正在执行的指令的地址或下一条要执行的指令的地址,这取决于具体的系
统。
上下文切换可以更详细地描述为内核(即操作系统的核心)对 CPU 上的进程
(包括线程)执行以下活动:
- 暂停一个进程的处理,并将该进程的 CPU 状态(即上下文)存储在内存中的
某个地方- 从内存中获取下一个进程的上下文,并在 CPU 的寄存器中恢复它
- 返回到程序计数器指示的位置(即返回到进程被中断的代码行)以恢复进
程。
- 从数据来说,以程序员的角度来看, 是方法调用过程中的各种局部的变量
与资源; 以线程的角度来看, 是方法的调用栈中存储的各类信息。
引发上下文切换的原因一般包括:线程、进程切换、系统调用等等。上下文
切换通常是计算密集型的,因为涉及一系列数据在各种寄存器、 缓存中的来回
拷贝。就 CPU 时间而言,一次上下文切换大概需要 5000~20000 个时钟周期,相
对一个简单指令几个乃至十几个左右的执行时钟周期,可以看出这个成本的巨大。
并行和并发
我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的并行车
辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可
以并行运行。CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数或者线
程数就相当于并排可以通行的车道;而多个 CPU 就相当于并排有多条高速公路,而
每个高速公路并排有多个车道。
当谈论并发的时候一定要加个单位时间,也就是说单位时间内并发量是多少?
离开了单位时间其实是没有意义的。- 综合来说:
并发 Concurrent:指应用能够交替执行不同的任务,比如单 CPU 核心下执行多
线程并非是同时执行多个任务,如果你开两个线程执行,就是在你几乎不可能察觉
到的速度不断去切换这两个任务,已达到"同时执行效果",其实并不是的,只是计算
机的速度太快,我们无法察觉到而已.
并行 Parallel:指应用能够同时执行不同的任务,例:吃饭的时候可以边吃饭边
打电话,这两件事情可以同时执行
线程的状态/生命周期
Java 中线程的状态分为 6 种:
- 初始(NEW):新创建了一个线程对象,但还没有调用 start()方法。
- 运行(RUNNABLE):Java 线程中将就绪(ready)和运行中(running)两种
状态笼统的称为“运行”。
线程对象创建后,其他线程(比如 main 线程)调用了该对象的 start()方法。
该状态的线程位于可运行线程池中,等待被线程调度选中,获取 CPU 的使用权,
此时处于就绪状态(ready)。就绪状态的线程在获得 CPU 时间片后变为运行中
状态(running)。 - 阻塞(BLOCKED):表示线程阻塞于锁。
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作
(通知或中断)。 - 超时等待(TIMED_WAITING):该状态不同于 WAITING,它可以在指定的时
间后自行返回。 - 终止(TERMINATED):表示该线程已经执行完毕。