建立多线程的方式
1)继承Thread类建立线程java
2)实现Runnable接口建立线程node
3)使用Callable和Future建立线程面试
4)使用线程池算法
start()和run()的区别
run()是线程对象执行的内容,start()是启动线程对象spring
如何建立一个线程池Excutorservice
- 核心线程数
- 当池中线程数小于核心线程数时建立新线程
- 核心线程数不会被回收
- 最大线程数
- 当池中线程数大于核心线程数小于最大线程数且队列满时建立新线程
- 大于核心线程数小于最大线程数的线程在空闲时间超过超时时间后会被回收
- 阻塞队列
- 当池中线程数大于等于核心线程数时任务会优先放入队列中
- 经常使用的4种线程池
- fixedThreadPool: 核心线程数和最大线程数定长,多的任务放入阻塞队列中
- cachedThreadPool: 核心线程数为0,最大线程数无限大,线程超过空闲时间时回收
- scheduledThreadPool: 核心线程数定长,最大线程数无限大,线程执行完任务即回收,可周期性执行
- singleThreadPool: 单线程,任务放入阻塞队列中,用于顺序执行
多线程同步有哪几种方法
- synchronized
- lock
- CountDownLatch
区别数据库
- synchronized是jvm层面的,而lock是第三方的类,都是可重入锁
- lock能够判断锁的状态,trylock能够设置超时,lockInterruptibly能够中断等待
synchronized锁代码块和锁方法有什么区别
- 修饰方法
- 修饰通常方法,锁住的是对象,不一样的对象是不一样的锁
- 修饰静态方法,锁住的是类,该类的全部对象共享这把锁
- 对象被锁时能够调用该对象的非同步方法
- 修饰代码块
- 做用范围是括号中的范围
- this: 锁住的是对象
- *.class: 锁住的是类
锁有哪些状态(sleep和wait有什么区别)
- 锁的状态
- 准备状态
- 就绪状态
- 运行状态
- 阻塞状态
- wait: object类,当前线程释放了对象monitor变成挂起状态,可用notify唤醒去抢夺对象monitor,多个线程等待时,notify只能随机唤醒一个,全唤醒须要使用notifyAll
- sleep: Thread类中,没有释放对象monitor,当前线程睡眠状态,时间到后自动唤醒
- 死亡状态
- ReentrantLock
- lock的具体实现,是可重入锁(同一个对象可重复递归调用的锁)
- 公平锁是线程获取锁的顺序是按照加锁顺序,非公平锁则是抢锁机制
什么是乐观锁,什么是悲观锁
- 悲观锁:老是假设最坏的状况,每次去拿数据的时候都认为别人会修改,因此每次在拿数据的时候都会上锁,如synchronized
- 乐观锁:每次去拿数据的时候都认为别人不会修改,因此不会上锁,可是在更新的时候会判断一下在此期间别人有没有去更新这个数据,能够使用版本号等机制。如atomic包下面的原子变量,使用了volatile原语,实现方式是CAS:
- V(需读写的内存位置)+A(准备用来比较的参数)+B(准备写入的新值),若A的参数与V的对应的值相匹配,就写入值B;若不匹配,就写入这个不匹配的值而非B。
一致性hash算法
一致性hash采用的是环状结构,hash出来的key落到顺时针最近的结点上,这样能够保证在结点增长删除的状况下影响最小,而为了应对平衡性问题采用了虚拟结点的方式,将实际结点数放大,使key更均匀的落到每一个结点上。设计模式
java线程池里面的arrayblockingqueue,linkedblockingqueue的用途和区别
- arrayblockingqueue是使用数组实现的必须指定长度,为了达到环,当索引下标等于数组长度时会归0,put和take操做共用同一把锁,数据的写入和读取只移动索引。
- linkedblockingqueue是使用链表实现的,每次写入会新增一个node放到链表尾部,读取则从表头删除node,因为put和take单独操做分别使用各自的锁,在效率上比arrayblockingqueue要高。