并发和并行
并发就是同一时刻只有一条指令在执行,但由于CPU时间片很小,多个指令间能快速切换,宏观上形成同时执行的效果。
并行则是真正意义上的同时进行。
并发三要素
- 原子性:在一个操作中,CPU 不可以在中途暂停然后再调度,即不被中断操作,要么执行完成,要么就不执行
- 可见性:多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
- 有序性:程序执行的顺序按照代码的先后顺序执行
线程和进程的区别
- 进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
- 每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小
- 一个进程可以有多个线程,至少有一个主线程
- 一个进程崩溃后,在保护模式下不会对其他进程产生影响;但一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
Q:为什么进程切换的消耗比线程切换大?
A:
进程拥有独立的内存空间和资源管理机制,每次切换时需要保存和恢复更多的上下文信息;
而线程共享同一进程的内存和大部分资源,切换时涉及的上下文信息较少。
且进程切换更多发生在内核态,而同一进程内的线程切换更多发生在用户态,因此开销相对较小。
多进程和多线程分别有何优势?
- 线程之间可以更轻松的共享数据
- 系统创建进程需要为该进程重新分配系统资源,故创建线程代价比较小
- 线程崩溃可能导致整个进程崩溃,而进程崩溃不会影响到另一个进程
哪些是线程共享的?
线程共享:
- 全局变量和堆
- 文件描述符,包括打开的文件、网络连接、输入输出流等,这些都是进程级别的资源
Q:Java三种创建线程的方法有什么区别?
A:
……
Java中锁的类型
- 悲观锁、乐观锁:悲观锁总是认为其他线程会修改数据,因此每次操作都上锁,如
synchronized
;乐观锁则假设其他线程不会修改数据,不上锁,如CAS
- 公平锁、非公平锁:公平锁按申请锁的顺序获取,如
ReentrantLock
;非公平锁不保证顺序 - 自旋锁:线程循环等待直到获取锁
- 可重入锁、不可重入锁:可重入锁允许内层使用锁,不会产生死锁;不可重入锁不允许
- 共享锁/读锁、独享锁/写锁:共享锁允许多个执行单元同时获取锁,独享锁只允许一个
线程池
ThreadPoolExecutor
的参数:
参数名 | 是否必需 | 数据类型 | |
---|---|---|---|
corePoolSize | 必需 | int | 线程池的核心线程数 |
maximumPoolSize | 必需 | int | 线程池的最大线程数 |
keepAliveTime | 必需 | long | 当线程数大于核心线程数时,多余的空闲线程存活的最长时间 |
unit | 必需 | TimeUnit | keepAliveTime的时间单位 |
workQueue | 必需 | BlockingQueue<Runnable> | 决定线程池的任务队列的类型 |
threadFactory | 可选 | ThreadFactory | 用于指定为线程池创建新线程的方式 |
handler | 可选 | RejectedExecutionHandler | 拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务 |
几种常见的内置线程池