面试题:

    1. 什么是进程和线程
      1. 进程是计算机获取资源的最小单位
      2. 线程是计算机调度、运行的最小单位
    2. 实现线程的三种方式
      1. 继承Thread类
      2. 实现runnable接口
        1. 覆写run()方法
        2. 实现类的实例交给thread类封装,调用有参构造生成线程
        3. 适合无返回值
      3. 实现callalbe接口
        1. 覆写cal()方法
        2. 实现类的实例交给futuretask类封装,再交给thread类封装,调用有参构造生成线程
        3. 适合有返回值
      4. 线程池
    3. 继承Thread类和实现runnable接口的区别?
      1. 相同点:
        1. thread类实现了后者
        2. 都重写了run方法
      2. 不同点
        1. 实现runnable接口不具有单继承的局限性,更加健壮
        2. 实现runnable接口可以更好地实现对资源的共享
    4. 线程的常用方法
      1. yeild方法:使当前线程从执行状态变为就绪状态——放弃锁资源
      2. sleep方法:使当前执行线程休眠,休眠结束可以放回执行状态——不放弃锁资源
      3. join方法:通常用于在main()主线程内,等待其它线程完成再结束main()主线程,不会放弃锁资源,线程会等
      4. deamon方法:为程序提供服务的线程,在非后台线程全部结束后,杀死所有的后台线程(PS:main() 属于非后台线程|使用setDaemon() 方法将一个线程设置为后台线程。)
    5. 线程状态:
      1. 新建状态:刚new 未调用start()方法
      2. 就绪状态:调用start()方法 处于就绪队列,等待jvm线程调用器调用
      3. 运行状态:当就绪状态的线程获取到资源,执行run()方法,处于运行状态。可以因为具体情况不同变为阻塞状态、就绪状态、死亡状态
      4. 阻塞状态:
        1. 等待阻塞:收到wait通信(用notify|notifyAll唤醒)
        2. 同步阻塞:请求的资源正被其他线程访问,获取锁资源失败
        3. 其他阻塞:通过调用线程的sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
      5. 死亡状态:完成任务或者其他终止条件发生
    6. 线程的通信方法
      1. wait-会释放锁(sleep() 和 yield() 并没有释放锁)
      2. notify
      3. notifyall
      4. PS:实际上,只有在同步控制方法或同步控制块里才能调用wait() 、notify() 和 notifyAll()。
    7. wait和sleep的区别
      1. Sleep是thread类中的静态方法 不会放弃锁资源 睡眠时间结束以后 直接回到运行
      2. Wait是object中的方法 它使线程处于等待状态放弃锁资源 需要通过notify或者notifyall进行唤醒Run方法与start方法
    8. synchronized锁作用
      1. 确保线程互斥地访问同步资源
      2. 保证共享变量的修改能够及时可见
      3. 有效地解决重排序问题
    9. synchronized实现原理
      1. 加锁:monitor enter
      2. 解锁:monitor exit
      3. 总结:synchronized基于jvm内置锁实现 依赖于操作系统的互斥锁。主要通过monitor enter和monitor exit实现。当有线程获取到资源的时候 monitor enter计数不为0 其他线程阻塞 当线程释放锁 monitor exit监视器将计数变为0 阻塞的线程此时可以竞争锁资源。Jdk1.5之后对锁进行了优化,锁有了一个升级的过程,Synchronized会从无锁升级为偏向锁,再升级为轻量级锁,最后升级为重量级锁。提升了性能。
    10. synchronized的优化+无锁、偏向锁、轻量级锁、重量级锁区别
      1. 偏向锁:单线程时无需重复上锁
      2. 轻量级锁:未获取锁资源的线程自旋——会产生忙等(其他线程原地自旋空耗cpu)
      3. 重量级锁:自旋超过最大自旋次数就挂起
    11. sychronized和lock锁的区别
      1. sychronized锁基于jvm的互斥锁实现,其锁状态无法判断,支持少量同步,可以自动释放锁
      2. lock是一个接口,我们常用其实现类进行加锁,可以判断锁状态,支持大量同步,需要手动在finally解锁,不然会造成死锁。
    12. 什么是Threadlocal?
      1. 称为线程本地变量,用作线程隔离,解决线程安全问题
      2. 通过为每个线程提供一个独立的变量副本解决并发访问的冲突问题
      3. 键值对形式,key为副本,value为我们存储的值。只有key和线程对应时才能取值,其他线程无法取值
    13. Threadlocal原理
      1. PS:ThreadLocalMap中用于存储数据的entry定义,使用了弱引用,可能造成内存泄漏。
      2. 解决方法:
        1. l 使用完线程共享变量后,显式调用ThreadLocalMap.remove方法清除线程共享变量;
        2. l ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。
    14. 线程池的作用
      1. 控制并发数量:并发过多抢占资源可能会导致阻塞
      2. 线程的复用:频繁得创建、销毁线程,会影响性能/效率
      3. 管理线程的声明周期
    15. 线程池的执行流程
      1. 总结:核心线程=> 等待队列 => 非核心线程 => 拒绝策略
      2. l 如果有空闲的线程直接使用,没有空闲的线程并且线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务
      3. 线程数量达到了corePoolSize,则将任务移入队,列等待空闲线程将其取出去执行(通过getTask方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源,整个getTask操作在自旋下完成),如果队列已满,新建线程(非核心线程)执行任务,空闲下来以后,非核心线程会按照时间策略进行销毁
      4. 队列已满,总线程数又达到了maximumPoolSize 最大线程数,就会执行任务拒绝策略。
    16. 线程池的七大核心参数
      1. 核心线程数
      2. 最大线程数
      3. 空闲时间长
      4. 空闲时间单位
      5. 工作队列
      6. 线程工厂(推荐 Executors.defaultThreadFactory)
      7. 拒绝策略
    17. 线程池的四种拒绝策略
      1. 报错并返回异常
      2. 报错不返回
      3. 抛弃旧任务
      4. 调用线程去执行
    18. 常见的四种线程池
      1. 可缓存 CachedThreadPool
        1. 没有核心线程 最大数量为Integer最大值
        2. 有空闲线程则复用,没有则新建
        3. 空闲时长为60s
        4. 适用负载轻、短期异步的任务
      2. 固定长 FixedThreadPoo
        1. 核心线程数==最大线程数
        2. 创建至最大线程数才会开始复用
        3. 适合长期任务
      3. 单个线程 SingleThreadPoo
        1. 仅有一个线程执行
        2. 任务遵循先进先出原则
        3. 适用于队列形式的任务
      4. 可调度 SingleThreadPoo
        1. 唯一有延迟执行和周期执行任务的线程池
        2. l 适用:周期性执行任务的场景(定期的同步数据)
      5. 自定义:略
    19. 线程池常用方法:
      1. 执行:
        1. excute:ThreadPoolExecutor的核心方法,提交一个任务到线程池执行
        2. Submit :实际调用的还是excute方法。通过future来获取任务执行结果
      2. 中断:
        1. shutdown:不会立即中断线程池,先暂停接受任务,等任务缓存队列的任务执行完后进行终止
        2. shutdownnow:立即进行终止,会尝试打断正在执行的任务,清空任务队列,返回未执行的任务
        3. isTerminated:调用shutdown方法后我们可以在一个死循环里面用isTerminated方法判断是否线程池中的所有线程已经执行完毕,如果子线程都结束了,我们就可以做关闭流等后续操作了。