线程有哪些状态
java的线程状态有6种:
操作系统中有5状态的说明
注意java的runnable对应了就绪、运行、阻塞I/O
线程池的核心参数
主要是说线程池的一个实习类 threadPoolExecutor.class
1.corePoolSize 核心线程数据(可以为0)
最多保留的线程数
2.maximumPoolSize最大线程数目
核心线程+急救线程
3.keepAliveTime生存时间
针对急救线程
4.unit时间单位
针对急救线程
5.workQueue
阻塞队列
6.threadFactory线程工厂
可以为线程创建时起好名字
7.handler拒绝策略
四种
线程池的工作流程:
sleep 和 wait的区别
共同点:wait() ,wait(long) ,sleep(long)都是让线程暂时放弃cpu的使用权,进入阻塞状态
不同点:
- .方法归属不同
1.sleep(long)是Thread的静态方法
2.而wait(),wait(long)都是Object的成员方法,每个对象都有 - 醒来的时机不同
1.执行sleep(long)和wait(long)的线程都会在等待响应毫秒后醒来
2.wait(long) 和 wait()还可以被notify唤醒,wait()如果不唤醒就一直等下去
3.他们都可以被打断唤醒 - 锁特性不同
1.wait方法的调用必须先获取wait对象的锁,而sleep则无此限制
2.wait方法执行后会释放对象锁,允许其他线程获得该对象锁(我放弃,但你们还可以用)
3.而sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃,你们也用不了)
lock vs synchronized
- 语法层面
- synchronized 是关键字,源码在jvm中,用c++语言实现的
- Lock是接口,源码由jdk提供,用java语言实现
- 使用synchronized时,退出同步代码块锁会自动释放,而使用Lock时,需要手动调用unlock方法释放锁
- 功能层面
- 二者均属于悲观锁、都具备基本的互斥、同步、锁重入功能
- Lock提供了许多synchronized不具备的功能,例如获取等待状态、公平锁、可打断、可超时、多条件变量
- Lock有适合不同场景的实现,如ReentrantLock、ReentrantReadWriteLock
- 性能层面
- 在没有竞争时,synchronized做了很多优化,如偏向锁、轻量级锁、性能不赖
- 在竞争激励时,Lock的实现通常会提供更好的性能
volatile 能否保证线程安全
1.线程安全要考虑三个方面:可见性、有序性、原子性
- 可见性指,一个线程对共享变量修改,另一个线程能看到最新的结果
- 有序性指,一个线程内部代码按编写顺序执行
- 原子性指,一个线程内多行代码以一个整体运行,期间不能有其他线程的代码插队
2.volatile能够保证共享变量的可见性和有序性,但并不能保证原子性 - 原子性举例
- 可见性举例 高速重复的并非计算下,被jit优化调了
- 有序性举例
悲观锁 vs 乐观锁
synchronized 和 lock 都是悲观锁的代表。
1.核心思想是,线程获取锁,才能操作共享变量。获取锁失败的线程,都得停下来等待。
2.线程从运行到阻塞,从阻塞到唤醒,涉及线程的上下文的切换,频繁切换,会影响性能。
3.synchronized 和 lock在获取锁失败的时候,会尝试重新获取几次。减少阻塞的机会
乐观锁的代表是 atomicinteger,是用cas(compare and swap一般要配合volatile一起使用的)来保证原子性
1.核心思想是无需加锁,每次只有一个线程能成功修改共享变量,其他失败的线程不需要停止,不断重试直至成功
2.由于线程一直运行,不需要阻塞,因此不涉及线程上下文切换。
3.它需要多核CPU支持,且线程不应超过CPU核数
HashTable vs ConcurrentHashMap
1.hashTable 和ConcurrentHashMap 都是线程安全的Map集合
2.Hashtable 并发度低,整个Hashtable对应一把锁,同一时刻,只能有一个线程操作它
3.1.8之前ConcurrentHashMap使用了segment+数组+链表的结构,每个Segment对应一把锁,如果多个线程访问不同的Segment,则不会冲突
4.1.9开始ConcurrentHashMap将数组的每个头节点作为锁,如果多个线程访问的头结点不同,则不会冲突
谈一谈对ThreadLocal的理解
1.ThreadLocal可以实现【资源对象】的线程隔离,让每个线程各用各的【资源对象】,避免争用引发的线程安全问题 。
2.ThreadLocal 同时实现了线程内的资源共享。
3.其原理是,每个线程内有一个ThreadLocalMap 类型的成员变量,用来存储资源对象
- 调用set方法,就是以ThreadLocal自己作为key,资源对象作为value,放入当前线程的ThreadLocalMap集合中
- 调用get方法,就是以ThreadLocal自己作为key,到当前线程中查找关联的资源值
- 调用remove方法,就是以ThreadLocal自己作为key,移除当前线程关联的资源值
4.ThreadLocalMap里面的key为什么要设计成弱引用
- thread 可能需要长时间运行(如线程池中的线程),如果key不再使用,需要在内存不足(GC)时释放其占用的内存
- 但GC仅是让key的内存释放,后续还要根据key是否为null来进一步释放值的内存,释放时机有:
- 获取key发现null key
- set key时,会使用启发式扫描,清除临近的null key,启发次数与元素个数,是否发现null key有关
- remove时(推荐),因为一般使用ThreadLocal时都把它作为静态变量,因此GC无法回收