线程
Thread状态
一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)
Thread常用方法
- start方法:start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。
- run方法:run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。
- sleep方法:sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。
- yield方法:调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
- join方法:假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的时间。join内部调用wait方法,会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。
- interrupt方法:interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程。中断运行状态的线程通常通过标志位来实现
线程属性的几个方法
- getId:用来得到线程ID
- getName和setName:用来得到或者设置线程名称。
- getPriority和setPriority:用来获取和设置线程优先级。
- setDaemon和isDaemon:用来设置线程是否成为守护线程和判断线程是否是守护线程。
什么是守护线程
守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。
wait()和sleep()的区别
三个线程,如何顺序打印ABC
volatile
volatile关键字干了什么?(什么叫指令重排)
保证可见性和禁止指令重排,不保证原子性 www.zhihu.com/question/32…
volatile的使用场景
volatile在DCL上的作用是什么?
instance=new Singleton();创建了一个对象。这一行代码可以分解为如下的3行伪代码。
memory = allocate(); // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory; // 3:设置instance指向刚分配的内存地址
上面3行伪代码中的2和3之间,可能会被重排序。 所以当instance被定义为 private volatile static Instance instance的话就会保证在创建对象的时候的执行顺序一定是1-2-3的步骤,从而在线程B访问第2处代码时候,instance要么为null 要么是已经完全初始化好的对象。
synchronized
sychronized修饰在不同的地方有什么区别?
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
- 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
- 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
synchronized的工作原理
synchronized 在 JVM 的实现原理是基于进入和退出管程(Monitor)对象来实现同步。但 synchronized 关键字实现同步代码块和同步方法的细节不一样,代码块同步是使用 monitorenter 和 monitorexit 指令实现的,方法同步通过调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。
synchronized的锁实际是保存在哪里
对象头
synchronized在JDK1.6之后做了哪些优化
synchronized 锁的优化:锁的四种状态-无锁,偏向锁、轻量级锁,重量级锁
ReentrantLock
可重入锁,加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。还可以实现公平锁机制
AtomicInteger
AtomicInteger的出现只是为了解决integer在多线程下线程安全的问题,方法都是原子操作。 AtomicInteger是块地址,可变的,拿来改的。
锁
重点:www.cnblogs.com/jyroy/p/113…
对象锁和类锁是否会互相影响
不会
同一个锁为什么效率有差别?
阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长。
CAS无锁编程的原理
CAS算法涉及到三个操作数:
- 需要读写的内存值 V。
- 进行比较的值 A。
- 要写入的新值 B。 当且仅当 V 的值等于 A 时,CAS通过原子方式用新值B来更新V的值(“比较+更新”整体是一个原子操作),否则不会执行任何操作。一般情况下,“更新”是一个不断重试的操作。
ReentrantLock的实现原理
AQS
AQS原理
www.cnblogs.com/zofun/p/122…
AQS内部实现了两个队列,一个同步队列,一个条件队列。
同步队列:当线程获取资源失败之后,就进入同步队列的尾部保持自旋等待,不断判断自己是否是链表的头节点,如果是头节点,就不断参试获取资源,获取成功后则退出同步队列。
条件队列:为Lock实现的一个基础同步器,并且一个线程可能会有多个条件队列,只有在使用了Condition才会存在条件队列。
有点难说不清,以后填坑
线程池
设计一个线程池,怎么设计线程池。(线程池参数)
- 线程池最大大小:IO密集型为2N,计算密集型为N+1
- 线程池维护最小线程数量:5
- 线程允许空闲时:2s
- 缓冲队列:大小
- 拒绝策略:缓冲队列满了怎么办
使用过哪些线程池
blankj的线程工具
getFixedPool : 获取固定线程池
getSinglePool : 获取单线程池
getCachedPool : 获取缓冲线程池
getIoPool : 获取 IO 线程池
getCpuPool : 获取 CPU 线程池
还有定时和周期性线程池
线程池的原理,有哪些拒绝策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
任务有优先级怎么办,知道优先级反转吗?
- 设置多个任务队列,按照任务优先级来处理。
- 优先级继承和优先级极限,不想多讲,老八股文了