实现线程的三种方式?
1:继承Thread类,覆写run方法,创建实例对象,Thread可以直接调用start来启动线程
2:创建runnable实现类,在类中覆写run方法,需要将实例作为参数传递给Thread类有参构造创建线程对象,
再通过Thread类来调用start方法。
3:创建callable接口实现类,类中覆写call方法,创建实例对象,将其作为参数传递给FutureTask类,然后有参构造FutureTask
对象,再通过这个对象来传递给Thread类的有参构造创建线程对象,再调用start方法来启动线程。
start()和run()的区别
都需要实现run方法,并且最后都是用start方法来启动线程
runable和callable的区别
callable有返回值,并且使用FutureTask来进行封装
2.Synchronized和lock的区别
相同:都是解决并发编程中的线程安全问题
不同:Synchronized是一个关键字 在线程发生异常时,会自动释放锁,不会发生异常死锁,可重入,不可判断,非公平锁
Lock是一个接口,它基于CAS乐观锁来实现的,在异常时不会自动释放锁,我们需要在finally中释放锁(需要手动释放锁)
是可重入,可判断的,可手动指定公平锁或者非公平锁
3.线程的生命周期 (new,runnable,running,Blocked,Dead)
sleep和wait的区别
第一: sleep方法是Thread类的静态方法,wait方法是Object类的方法
第二:sleep方法不会释放对象锁,wait方法会释放对象锁
第三:sleep方法必须捕获异常,wait方法不需要捕获异常
第四:wait需要被唤醒notify
sleep和wait,被唤醒,进入runnable状态
4.synchronized锁的原理,JDK1.6之后做了什么样的优化
是基于JVM内置锁实现,通过内部对象Monitor(监视器锁)实现 ,监视器锁的实现依赖 底层操作系统的Mutex lock(互斥锁)实现
它是一个重量级锁性能较低,涉及到用户态到内核态的切换,会让整个程序性能变得很差
JDK1.6后增加了锁升级的过程,依次为无锁,偏向锁,轻量级锁,重量级锁。而且还增加了锁粗化,锁消除等策略,这就节省了锁操作的开销,
提高了性能(没有线程抢占资源无锁,轻量级锁自旋10次变成重量级锁)
5.乐观锁的使用场景(数据库,ES) version Atomic
场景一:ES中对version的控制并发写。
场景二:数据库中使用version版本号控制来防止更新覆盖问题。
场景三:原子类中的CompareAndSwap操作
13.Synchronized和volatile的区别
相同点:都是解决并发编程中线程安全问题
不同点:
第一:volatile的实现原理,是在每次使用变量时都必须从主存中加载,修改变量后都必须立马同步到主存;
synchronized的实现原理,则是锁定当前变量,让其他线程处于阻塞状态
第二:volatile只能修饰变量,
synchronized用在修饰方法和同步代码块中
第三:volatile修饰的变量,不会被编译器进行指令重排序,
synchronized不会限制指令重排序
第四:volatile不会造成线程阻塞,高并发时性能更高,
synchronized会造成线程阻塞,高并发效率低
第五:volatile不能保证操作的原子性,因此它不能保证线程的安全,
synchronized能保证操作的原子性,保证线程的安全
6.什么是CAS 比较并交换
乐观锁 compare and swap的缩写
非阻塞同步:基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共 享数据,那操作就成功了;
如果共享数据有争用,产生了冲突,那就再采取其他的补偿措施(最常见 的补偿措施就是不断地重试,直到成功为止),
7.AtomicInteger怎么保证并发线程安全性? 乐观锁 CAS
通过CAS操作原理来实现的 比较并交换
8.什么是乐观锁,什么是悲观锁,什么是重入锁,什么是自旋锁,什么是阻塞锁
比较并交换,不锁定资源
每次去拿数据的时候都会认为别人会修改数据,所以每次都会上锁,阻止别人来拿数据,直到悲观锁被释放。
允许同一个线程多次获取同一把锁,是为可重入锁
是一种状态,当一个线程尝试去获取锁时,如果这个锁已经被占用了,该线程就处于等待状态,并且隔一段时间再次尝试去获取
阻塞,指的是当一个线程尝试获取锁失败了,线程就就进行阻塞,这是需要操作系统切换CPU状态的
9.你用过JUC包中的类吗?说几个 Lock ConcurrentHashMap AtomicInteger
Lock锁体系 ,ConcurrentHashMap ,Atomic原子类,
10.ThreadLocal的作用
叫做线程本地变量,它是为了解决线程安全问题的,它通过为每个线程提供一个独立的变量副本,
来解决并发访问冲突问题 - 简单理解它可以把一个变量绑定到当前线程中,达到线程间数据隔离目的。
11.ThreadLocal的原理 ThreadLocalMap
ThredLocal是和当前线程有关系的,每个线程内部都有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,
它用来存储每个线程中的变量副本,key就是ThreadLocal变量,value就是变量副本。
12.ThreadLocal的使用场景 SecurityContextHolder、RequestContextHolder Zuul-RequestContext
比如在spring security中,我们使用SecurityContextHolder来获取SecurityContext,比如在springMVC中,
我们通过RequestContextHolder来获取当前请求,比如在 zuul中,我们通过ContextHolder来获取当前请求
14.线程池的作用,常用的线程池有几种?
主要作用是控制并发数量,线程池的队列可以缓冲请求
线程池可以实现线程的复用效果
使用线程池能管理线程的生命周期
4种
CachedThreadPool:可缓存的线程池,它在创建的时候,没有核心线程,线程最大数量是Integer最大值,最大空闲时间是60S
FixedThreadPool:固定长度的线程池,它的最大线程数等于核心线程数,此时没有最大空闲时长为0
SingleThreadPool:单个线程的线程池,它的核心线程和最大线程数都是1,也就是说所有任务都串行的执行
ScheduledThreadPool:可调度的线程池,它的最大线程数是Integer的最大值,默认最长等待时间是10S,它是一个由延迟执行和周期执行的线程池
15.线程池的执行流程
核心线程-->排队-->非核心线程-->拒绝策略
16.线程池构造器的7个核心参数
int corePoolSize, 核心线程数
int maximumPoolSize, 最大线程数
long keepAliveTime, 线程的空闲有效时间
TimeUnit unit, 线程的空闲有效时间单位
BlockingQueue<Runnable> workQueue, 工作队列
ThreadFactory threadFactory, 线程工厂
RejectedExecutionHandler handler 拒绝策略(容错方案)
17.线程池中的最大线程数
考虑CPU核数,并发数量综合来考虑
18.EurekaClient拉取注册表&心跳续约用到了什么技术来实现的?
ScheduledThreadPoolExecuto 带定时任务的线程池