1. 进程和线程的区别
进程是资源分配的最小单位,线程是cpu调度的最小单位
线程不能看做独立应用,而进程可以看做独立应用
进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径
线程没有独立的地址空间,多进程程序比多线程程序健壮
进程切换比线程切换开销大。
2. Thread的start和run方法区别
start:通过jvm的startThread创建一个线程,去调用run方法 run:普通方法,还是主线程执行
3. 如何给run方法传参
①构造函数传参②成员变量传参③回调函数传参
4.如何处理线程的返回值
①主线程等待法 (等待直到满足条件,没等到就sleep)
②使用Thread.join阻塞当前线程以等待子线程处理完毕。
③通过callable接口实现:通过futuretask or 线程池获取
4.如何停止线程
已经废弃:stop、suspend、resume
使用:调用interrupt()方法,通知线程应该中断了。 1、如果线程处于被阻塞状态,那么线程立即退出被阻塞状态,并抛出一个InterruptedExecption异常
2、如果正常状态,那么会将该线程中断标志设置为true,线程还是正常运行,不受影响。
5.线程的生命周期
1、新建(new):创建后尚未启动线程状态。
2、运行(Runnable):包含Running和Ready
3、无限期等待(waiting):不会被分配cpu执行时间,需要显示被唤醒
没有设置timeout参数的Object.wait()
没有设置timeout参数的Thread.join()
LockSupport.park()
4、限期等待(timed waiting):在一定时间后由系统自动唤醒 Thread.sleep()
设置timeout参数的Object.wait()
设置timeout参数的Thread.join()
LockSupport.parkNanos()
LockSupport.parkUntil() 5、阻塞(Blocked):等待获取排他锁
6、结束(Terminated):线程已经结束执行
6.wait 什么情况下会唤醒
1、另一个线程调用这个对象的notify方法,且刚好唤醒的是本线程
2、另一个线程调用这个对象的notifyAll方法
3、过了wait的超时时间,如果传入0就是永久等待。
4、线程自身调用了interrupt方法
6.sleep和wait的区别
sleep是Thread类的方法,wait是object类的方法。
sleep可以在任何地方使用
wait只能在synchronized方法或synchronized代码块中使用
本质区别:sleep只会让出cpu,不会导致锁行为改变,wait不仅让出cpu,而且会释放锁。
7.notify和notifyAll的区别
锁池 等待池
notifyAll会让所有处于等待池线程全部进入锁池去竞争获取锁机会。
notify只会随机选取一个处于等待池中线程进入锁池去竞争获取锁机会。
8.join
新线程加入了我们,所以我们等他执行完再出发。
9.volatile
volatile是一种同步机制,比synchronized和Lock相关类更轻量,因为使用它并不会发生上下文切换等开销大的行为。
如果一个变量被修饰成volatile,那么jvm就知道这个变量可能会被并发修改。
开销小,作用也小,场景有限。
volatile的使用场合
不适用:a++; 适用:1、自始至终只被各个线程赋值(因为赋值自身是原子性的,而volatile又保证了可见性,所以就保证了线程安全)
2、作为刷新之前变量的触发器
可见性:读一个volatile变量之前,需要先使相应的本地缓存失效,这样就必须到主内存读 取最新值,写一个volatile属性会立即刷到主内存。
禁止重排序:禁止指令重排序优化。例:解决单例双重锁乱序问题。
10.synchronized
锁的不是代码,是对象
对象锁
1、同步代码块 (Synchronized(this) synchronized(类实例对象))
2、同步非静态方法:锁的是当前对象的实例对象
类锁
1、同步代码块 synchronized(类.class) 2、 同步静态方法
类锁和对象锁互不干扰 ,理解不同对象。
10.synchronized和ReentrantLock的区别
一个是关键字,一个是对象
后者可设置获取锁等待时间,避免死锁,而且可以获取各种锁的信息。
11.JMM
java内存模型它是一种规范;是关键字和工具类的原理;重点:重排序、可见性、原子性。
并发过程中如何处理可见性、原子性、有序性的问题?
并发过程中两个关键问题?
a、线程之间如何通信? wait、notify、notifyAll
1、共享内存 隐式通信
2、消息传递 显示通信
b、线程之间如何同步?(操作的顺序)
在共享内存的并发模型中,同步是显示做的,如synchronize。
在消息传递的并发模型中,由于消息的发送必须在消息接收之前,所以同步是隐式。
什么是重排序?重排序好处能提高处理速度
重排序的三种情况:编译器优化(包括jvm、jit编译器等)、cpu指令重排、内存的“重排序”(线程a的修改线程b看不到,引出可见性问题)。
为什么有可见性问题?
cpu有多级缓存,导致读的数据过期
- 高速缓存的容量比主内存小,但是速度仅次于寄存器,所以在cpu和主内存之间就多了cache层。
- 线程间的对于共享变量的可见性问题不是由多核引起的,而是由多缓存引起的。
- 如果所有核心都只用一个缓存,那么也就不存在内存可见性的的问题了。
- 每个核心都会将自己需要的数据读到独占缓存中,数据修改后也是写入缓存中,然后等待刷入到主缓存中,所以会导致有些核心读取到的是一个过期的值。
java中原子性操作
单例模式
Happens-Before规则
1、单线程规则
2、锁操作
3、volatile变量
4、线程启动
5、线程join
6、传递性
7、中断
1. 线程池
1、为什么要使用线程池?
- 降低消耗资源
- 提高线程的可管理性
2、juc的三个Executor接口
- Executor:运行新任务的简单接口,将任务提交和任务执行细节解耦。
- ExecutorService:具备管理执行器和任务生命周期的方法,提交任务机制更完善。
- ScheduledExecutorService:支持future和定期执行任务。
3、ThreadPoolExecutor的构造函数
corePoolSize:核心线程数量
maximumPoolSize:线程不够用时能够创建最大的线程数。
workQueue:任务等待队列
keepAliveTime:抢占的顺序不一定看运气
threadFactory:创建新线程
handler:线程池的饱和策略
4、线程池的状态
RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务
SHUTDOWN:不再接受新提交的任务,但可以处理存量任务
STOP:不再接受新提交任务,也不处理存量任务
TIDYING:所有任务已终止
TERMINATED:terminated()方法执行完后进入该状态
5、线程池的大小如何选定
cpu密集型:线程数=按照核数或者核数+1
i/o密集型:线程数=cpu核数*(1+平均等待时间/平均工作时间)