1.进程通信和线程通信
进程间通信:
- 管道:文件形式的缓冲区,一端为读端,一端为写端,只能在父子进程间通信,且是单向的
- 命名管道:FIFO文件,任意两个进程间都可以使用,同样是单向的
- 共享内存
- 信号
- 消息队列
- Socket:用于不同机器进程间的通信,在同一台机器上的进程也可以通过socket进行通信
多个进程同时读写一个文件可以使用文件锁,flock
线程通信:
共享内存和消息传递两种方式:
- 在共享内存的并发模型中,线程之间通信是通过共享内存来实现的,是隐式的通信
- 在消息传递模型中,线程之间通信是显示的通过发送消息来进行通信,比如Actor模型,就是通过mailbox来进行通信,mailbox的本质是队列
2.进程同步和线程同步
- 进程同步:信号量、互斥量、管程、文件锁
3.管程
4.进程有哪些状态?线程有哪些状态?
**进程五个状态:**New、Ready、Running、Blocked、Terminated
**线程七个状态:**New、Ready、Running、Waiting、Timed-Waiting、Blocked、Terminated
5.Sleep和Wait函数的异同?
相同点:
- Sleep和Wait都会让出cpu资源
- Sleep()和Wait()都会是线程进入Waiting状态
不同点:
- Sleep()是Thread类的方法,而Wait()是Object类的方法
- Sleep()不会释放锁,而Wait()会释放锁
- Sleep()到达一定的时间就会进入Ready状态,而Wait()则需要Notify()/NotifyAll()唤醒
6. volatile和Synchronized关键字
volatile关键字两大特性:
- 保证内存可见性
- 禁止指令重排,保证了有序性
在JMM中,规定了线程和主内存之间的抽象关系,每个线程都有自己的本地内存,线程之间共享主内存,本地内存中存储该线程已读写的共享变量的副本
使用volatile关键字修饰的变量会在写操作后立即更新到主内存中,其他读取此变量的线程在每次读取前都会从主内存中读取最新的值,由此可以保证内存可见性
volatile实现禁止指令重排是通过内存屏障来实现的,在编译器生成字节码时,插入特定的内存屏障来禁止重排
内存屏障类型:StoreStore、StoreLoad、LoadLoad、LoadStore
由于volatile不能保证原子性,所以无法保证线程安全
Synchronized关键字:
- 保证可见性
- 保证原子性
所以synchronized关键字可以保证线程安全性
synchronized关键字可以用在普通同步方法、静态方法、和方法块中
锁升级过程:
偏向锁:使用的等到竞争出现时才释放锁的机制,当一个线程获取锁时,会在对象头中记录偏向线程id,这个线程以后再获取锁时不需要通过CAS操作,当竞争出现时,升级为轻量级锁
轻量级锁:线程会有个栈帧用来存储锁记录,线程将对象头中的Mark Word复制到这个栈帧当中,然后使用CAS操作将对象头中的Mark Word替换成指向这个栈帧的指针,成功的话就获取到锁了,失败的话会自旋重试
重量级锁:升级为重量级锁,锁标志位变为10,Mark Word存储的是指向重量级锁的指针(即为操作系统的mutex lock,之所以为重量级是因为使用mutex lock需要从用户态切换到内核态)
synchronized底层实现原理:
synchronized关键字可以修饰方法和语句块
在修饰语句块的时候,当java源代码被编译成字节码的时候,会在同步块的开始和结束的地方插入monitor_enter和monitor_exit字节码指令
在修饰方法的时候,并不会在字节码编译期间做什么操作,而是在class文件方法表中的access_flag字段中的synchronized关键字置为1
7.线程池
为什么要使用线程池?
- 提高线程响应速度:任务到达不需要等待线程创建
- 提高线程可管理性:统一分配、调优和监控
- 降低资源消耗:可以重复利用已创建的线程
线程池核心参数
- corePoolSize:线程池中核心线程的数量
- maxMumPoolSize:线程池中允许的最大线程池数量
- keepAliveTime:线程池中的线程空闲后保持存活的时间
- TimeUint:时间单位
- workQueue:用来保存等待执行任务的阻塞队列,包括、LinkedBlockingQueue、synchronousQueue和priorityBlockingQueue
- ArrayBlockingQueue:基于数组的有界阻塞队列,按照FIFO顺序对元素进行排序
- LinkedBlockingQueue:基于链表的阻塞队列,同样是FIFO,静态工厂方法Executors的newFixedThreadPool()使用的就是这个队列
- SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,静态工厂方法newCachedThreadPool用的就是这个队列
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列
- ThreadFactory:用于设置创建线程的工厂
- Handler:线程池的拒绝策略
- AbortPolicy:抛出异常
- DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并执行当前任务
- DiscardPolicy:不处理,直接丢弃
- CallerRunsPolicy:用调用者所在的线程来执行任务
8.AQS
9.JUC组件
- countLatch
- cylicbarrier
- semaphore
- exchanger
10.线程模型
- 多对一:用户线程和内核线程N:1的关系,也就是一个内核线程对应着多个用户线程,用户线程的创建、调度、同步等所有细节都交由用户线程库来实现
- 优点:用户线程的很多操作对内核来说是透明的,没有用户态和内核态的切换,速度快,开销小
- 缺点
- 无法利用多个核心
- 一个线程阻塞,其他线程也无法执行
- 一对一:一个用户线程对应一个内核线程,Java采用的线程模型
- 优点:实现比较简单
- 缺点:
- 用户态和内核态切换带来的开销;影响系统性能
- 多对多模型:多个用户线程对应多个内核线程,且用户线程数一般多于内核线程数,实际上就是每个用户线程可以绑定多个内核线程,具体使用时动态绑定,比如某个内核线程因为用户线程的阻塞而让出cpu,那么其绑定的线程可以从其余绑定的内核线程中选择
- 优点:
- 具有多对一模型的轻量
- 由于对应多个内核线程,可以利用多核优势
- 可以实现完整的调度、优先级等
- 优点: