并行和并发有什么区别?
并行:多个线程,一个cpu一件一件处理
并发:多个线程,多个cpu同时运行
线程和进程的区别
一个程序至少1个线程,一个进程至少一个线程,进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高,进程是线程的一个实体,是cpu调度和分派的基本单位,势必程序更小的能独立运行的单位,同一个线程之间可以并发执行
创建线程的方式
通过继承Thread类创建线程
- 创建Thread类的子类,重写run方法。因此把run()方法称为执行体
- 创建Thread子类的实例,创建了线程对象
- 调用线程对象的start()接口启动线程
通过Runnable接口创建线程
- 定义runnable接口的实现类,并重写该接口run()方法
- 创建Runnable实现类的实例,并一次实例作为Thread的target来创建Thread对象
- 调用线程对象的start()接口启动线程
通过Callable和Future创建线程
- 创建Callable接口的实现类,并实现call()方法,具有返回值
- 创建Callable实现类的实例,使用Future Task类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值
- 使用FUture Task对象作为Thread对象的target创建并启动新的线程
- 调用Future Tsk对象的get()ff来获得子类线程执行结束后会有返回值
runnable和callable 的区别
Runnable的run()返回为void,他只是纯粹的执行run()方法中的代码而已
Callable接口中的call()方法是有返回值的,是一个泛型,可以用来获取异步执行的结果
sleep()和wait()方法的区别
sleep()是线程类(Thread)的静态方法,让调用者进入睡眠状态,直到时间结束之后运行,不会释放锁
wait()是Object的方法,可以释放锁,直到被唤醒才运行
创建线程池
固定长度,超过最大数量规模不再变化,线程结束补充新的线程
可缓存的线程池,超过规模回收空闲进程,需求增加自动添加新线程,规模不存在限制
单线程的线程池
创建一个固定长度的线程池,以延时或定时的方法执行任务类似Timer
线程池的状态
Runnin运行、ShutDown关机、Stop停止、Tidying整理、Terminated终止。
Runnin运行:线程池处于运行状态,能够接收新任务,以及对已添加的任务进行处理。
ShutDown关机:
- 在使用了shotdown()命令之后进入关机状态,
- 此时线程池不接收新任务,但能处理已添加的任务。
Stop停止:
- 不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
- 在使用了shoutdownNow()之后进入停止状态
整理:
- 线程池队列为空,执行中的任务也为空进入整理状态
- 此时可以执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重写terminated()函数来实现。
终止:
- 线程池彻底终止,就变成TERMINATED状态。
- Terminated()命令后线程池被彻底终止
submit()和execute() 的区别
接收的参数不同
submit()有返回值,execute()没有
submit()方便异常处理
如何保证多线程的运行安全?
可见性:一个线程对主内存的修改可以被其他线程看到
原子性:同一时刻只有一个线程可以对数据进行操作
有序性:一个线程观察其他现场的指令执行顺序,由于指令重新排序,该结果一般杂乱无序。
多线程锁的升级原理:
无锁——偏向锁——轻量级锁——重量级锁
死锁的四个必要条件
互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他的进程访问该资源,只能等待,直到占有该资源的进程使用完成之后释放该资源
请求和保持条件:进程获得一定的资源之后,又对其他的资源发出请求,但是该资源可能被其他的进程占有,此时请求阻塞,但又对自己获得的资源保持不变
不可剥夺条件:该线程获取的资源,在未完成使用之前,不可被剥夺,只能在使用完之后自己释放
环路等待条件:是进程发生死锁之后,若干进程之间形成一种头尾相接等待资源的关系
ThreadLocal是什么,有那些使用场景?
线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。java提供ThreadLocal类来支持线程局部变量。是一种实现线程安全的方式,但是在管理环境下,工作现场的生命周期比任何应用变量的生命周期都要长,任何线程局部变量一旦在工作完成之后没有被释放,java应用就存在内存泄漏的风险
synchronized底层实现原理
synchronized可以保证同一时刻只有一个方法可以进入到临界区,同时他还可以保证共享变量的内存的可见性
可以做用到普通同步方法,静态同步方法,方法同步块
synchronized 和 volatile 的区别是什么?
volatile本质实在是告诉jvm当前变量在寄存器(工作期间)内的值是不确定的,需要从主存种读取
synchroized是锁定当前变量,只有当前线程可以访问改变量,其他线程被阻塞
volatile仅可在变量种使用
synchronized可以使用在方法,变量,类上
volatile不会造成线程的阻塞,synchronized会造成阻塞
volatile标记的变量不会被编译器优化,synchronized会被编译器优化
synchronized和lock有什么区别?
首先synchronized是内置的关键字,在jvm层面上,Lock是一个类
synchronized无法判断所的状态,lock可以判断
synchronized会自动释放锁,lock不会主动释放锁,需要在finally中释放
synchronized被阻塞,那么剩下的线程就会一直阻塞。lock不一定会一直等下去,如果获得不到锁,线程可能不用一致等待就结束了
synchronized可重入,非公平,lock锁可重入,可判断,可公平
lock适合大量的代码同步问题,synchronized适合少量代码的同步问题
synchronized和ReentranLock的区别是什么
synchronized是关键字,ReentranLock是类更加灵活,可以被继承
ReentranLock可以对获取锁的时间进行设置,避免死锁
ReentranLock可以获取各种锁的信息
ReentranLock可以更灵活的实现多路通知
锁的机制不同ReentranLock底层调用的是Unsafe的park方法加锁
synchronized操作的应该是对象头中的mark word
atomic 的原理
atomic包中的类的基本特性就是在多线程环境下,当有多个线程同时对单个(包括基本类型及引用类型)变量进行操作时,具有排他性,即当多个线程同时对该变量值进行更新时,仅有一个线程能成功,而未成功的线程可以像自旋锁一样继续尝试,直到执行成功
atomic系列的类中的核心方法都会调用unsafe类中的几个本地方法。我们需要先直到一个东西就是Unsafe类,全名为sun.misc.Unsafe,这个类包含了大量的对c代码的操作,包括很多直接内存分配即原子操作的调用,而它之所以是非安全的,是告诉你这个方法的大量操作都存在安全隐患,需要小心使用,否者会有严重的后果。例如在通过unsafe分配内存的时候,如果自己指定某些区域可能会导致一些类似C++一样的指针越界到其他进程的问题。