多线程
1.并行和并发有什么区别?
并行:多个处理器或多核处理器同时处理多个任务。
并发:多个任务在同一个CPU核上,按细分时间片轮流(交替)执行,从逻辑上看那些任务是同时执行的。
2.线程和进程的区别?
一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。
3.守护线程是什么?
守护线程是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或者等待处理某些发生的事件。在Java中垃圾回收线程就是守护线程。
4.创建线程有几种方式?
三种方式:
- 继承Thread重写run方法。
- 实现Runnable接口。
- 实现Callable接口。
5.runnable和callable有什么区别?
runnable没有返回值,callable有返回值,callable可以看做是runnable的补充。
6.线程有哪些状态?
线程的状态:
- NEW:未启动。
- RUNNABLE:正在执行。
- BLOCKED:阻塞(被同步锁或者IO锁阻塞)。
- WAITING:永久等待。
- TIMED_WAITING:等待指定的时间然后重新被唤醒。
- TERMINATED:执行完成。
7.sleep()和wait()有什么区别?
类不同:sleep来自Thread,wait来自Object。
释放锁:sleep不释放锁,wait释放锁。
用法不同:sleep时间到会自动恢复,wait可以使用notify/nottifyAll方法唤醒。
8.notify()和notifyAll()有什么区别?
notify只会唤醒一个线程,notify具体唤醒哪一个线程有虚拟机控制。notifyAll会唤醒所以线程,notifyAll将全部线程从等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,不成功则留到锁池等待锁被释放后再次参与竞争。
9.线程的run()和start()有什么区别?
start方法用于启动线程,run方法用于执行线程的运行时代码。run方法可以重复调用,start方法只能调用一次。
10.创建线程池有哪几种方式?
创建线程池有七种方式,最核心是最后一种:
- newSingleThreadExecutor:单线程化的线程池,即它只用唯一的一个工作线程来完成任务。这样可以保证所有任务串行执行。
- newCachedThreadPool:可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool:定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newSingleThreadScheduledExecutor:创建单线程池,返回ScheduledExecutorService,可以进⾏定时或周期性的⼯作调度。
- newScheduledThreadPool:和newSingleThreadScheduledExecutor()类似,创建的是个 ScheduledExecutorService,可以进⾏定时或周期性的⼯作调度,区别在于单一工作作线程还是多个工作线程。
- newWorkStealingPool:Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利⽤Work-Stealing算法,并行地处理任务,不保证处理顺序。
- ThreadPoolExecutor:是最原始的线程池创建,上面1~3创建方式都是对ThreadPoolExecutor的封装。
11.线程池有哪些状态?
线程池有五种状态:
- RUNNING:接受新的任务,处理等待队列中的任务。
- SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。
- STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。
- TIDYING:所有任务都销毁了,workCount为0,线程池状态在转为TIDYING状态时,会执行钩子方法terminated()。
- TERMINATED:terminated方法结束后状态变成这个。
12.线程池中submit和execute方法有什么区别?
submit:可以执行Runnable和Callable任务。
execute:只能执行Runnable任务。
13.Java程序中怎么保证多线程的运行安全?
有三种方式:
- 使用安全类,比如java.util.concurrent下的类。
- 使用自动锁synchronized。
- 使用手动锁Lock。
14.多线程中synchronized锁升级的原理?
synchronized锁升级原理:在锁对象的对象头里面有一个threadid字段,在第一次访问的时候threadid为空,jvm让其持有偏向锁并将threadid设置成线程id,再次进入的时候会先判断threadid是否与其线程id一致,如果一致则可以直接使用此对象,不一致则升级偏向锁为轻量级锁,通过自旋循环一定次数获取锁,执行一定次数后如果还是没有获取到要使用的对象,就会把轻量级锁升级为重量级锁,这个过程就构成了synchronized锁的升级。
锁升级的目的:锁升级是为了减低锁带来的性能消耗。Java 6之后优化synchronized的实现方式,使用了偏向锁升级为轻量级锁在升级到重量级锁的方式,从而减低了锁带来的性能消耗。
15.什么是死锁?
死锁:是指多个线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,它们都将无法推进下去。
举例:线程A持有独占锁a,并尝试获取独占锁b的同时,线程B持有独占锁b,并尝试获取独占锁a的情况下,就会发生AB两个线程由于互相持有对方需要的锁,而发生堵塞现象,称之为死锁。
16.怎么防止死锁?
尽量使用ReentrantLock、ReentrantReadWriteLock类下的tryLock方法(),设置超时时间,超时可以退出防止死锁。
尽量使用java.util.concurrent并发类代替自己手写锁。
尽量降低锁的使用粒度,不要几个功能使用一把锁。
尽量减少同步的代码块。
17.ThreadLocal是什么?有哪些使用场景?
ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。
ThreadLocal的经典使用场景是数据库连接和session管理等。
18.说一下synchronized的实现原理?
synchronized是由一对monitorexit、monitorexit(监控进入、监控退出)指令实现的,monitor对象是同步的基本实现单元。Java 6的时候,Java虚拟机提供了三种不同的monitor实现,也就是常说的三种不同的锁:偏向锁、轻量级锁、重量级锁,大大的改进了其的性能。
19.synchronized和volatile的区别是什么?
synchronized可以修饰类、方法、代码块,volatile是变量修饰符。
synchronized可以保证变量修改的可见性、原子性,volatile只能保证变量修改的可见性。
synchronized可能会造成线程的阻塞,volatile不会造成线程的阻塞。
20.synchronized和Lock有什么区别?
synchronized可以给类、方法、代码块加锁,Lock只能给代码块加锁。
synchronized不需要手动释放锁,发生异常会自动释放锁,不会造成死锁,Lock则需要手动加锁和释放锁,如果使用不正确没有调用unLock方法释放锁就会造成死锁。
synchronized无法知道有没有成功获取锁,而Lock可以知道。
21.synchronized和reentrantLock的区别是什么?
ReentrantLock使用比较灵活,但是必须有释放锁的配合动作。
ReentrantLock必须手动获取和释放锁,而synchronized不需要手动释放和开启锁。
ReentrantLock只适用于代码块,而synchronized可用于修饰类、方法、代码块。
ReentrantLock标记的变量不会被编译器优化,synchronized标记的变量可以被编译器优化。
22.说一下atomic的原理?
atomic主要利用CAS(Compare And Wwap)、volatile、native方法保证原子操作,从而避免synchronized的高开销,从而提升执行效率。