Java高频面试题复盘系列之线程

70 阅读6分钟

Java高频面试题复盘系列之线程

线程的创建?

     线程创建的方式
         1 继承Thread类, 重写run()方法
         2 实现Runnable接口, 重写run()方法
         3 实现Callable接口, 重写call方法
         4 使用线程池

开启?

     启动线程, 对应上面四种方式
         1 创建自定义类的对象, 使用start()方法
             1 new 自定义类().start()
         2 创建Thread类对象, 把Runnable接口的实现类作为参数传递过去
             1 new Thread(new Runnable).start()
         3 new Thread(new FutureTask<E>(new Callable())).start();

状态?

     线程状态
         1 NEW: 新建状态
         2 Runnable: 可运行状态
         3 Blocked: 锁阻塞状态
         4 Waitting: 无限等待状态
         5 Timed waiting: 计时等待状态
         6 Terminated: 被终止状态
         状态
             创建, 就绪, 运行, 阻塞, 死亡

sleep和wait的区别, 线程池?

     如何停止一个线程?
         1 没有直接停止线程的方法, 只能等线程运行完毕
         2 如果要手动停止, 你可以使用volatile布尔变量来退出run()方法的循环或者是取消任务来中断线程
       sleep和wait的区别
         1 sleep: 让当前线程睡眠, 在同步方法或代码块中不释放锁
             1 该方法可以写在任何位置
         2 wait: 让当前线程等待, 同时释放已经拿到的锁
             1 该方法必须写在同步方法或同步块中
       线程池
         1 线程池的组成
             1 线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
             2 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
             3 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
             4 任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
         2 一个服务器完成一项任务所需时间为: T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。
             1 如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。
         3 线程池类ThreadPool
             1 默认初始化5个工作线程
             2 我们可以通过execute(List<Runnable> task)方法将自己的任务传递进去
                 1 或使用其他重载的方法
                     execute(Runnable task)
                     execute(Runnable[] task)
                 1 自己的任务实现Runnable接口, 重写其中的run()方法
                 2 但只是把自己的任务加入了线程池, 什么时候执行由线程池决定
         4 线程池有几种
             1 Executors.newFixedThreadPool, 创建固定线程数的线程池,使用的是LinkedBlockingQueue无界队列,线程池中实际线程数永远不会变化
             2 Executors.newSingleThreadExecutor (单线程线程池),创建只有一个线程的线程池,使用的是LinkedBlockingQueue无界队列,线程池中实际线程数只有一个
             3 Executors.newCachedThreadPool (弹性缓存线程池),创建可供缓存的线程池,该线程池中的线程空闲时间超过60s会自动销毁,使用的是SynchronousQueue特殊无界队列
             4 Executors.newScheduledThreadPool  (定时器线程池),创建可供调度使用的线程池(可延时启动,定时启动),使用的是DelayWorkQueue无界延时队列
             5 Executors.newWorkStealingPool 拥有多个任务队列的线程池,jdk1.8提供的线程池,底层使用的是ForkJoinPool实现,创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu核数的线程来并行执行任务
             6 ForkJoinPool
         5 线程池参数
             corePoolSize,线程池中的常驻核心线程数
             int maximumPoolSize,线程池能够容纳同时执行的最大线程数,此值必须大于等于1
             long keepAliveTime,多余的空闲线程的存活时间。
             TimeUnit unit,keepAliveTime的单位
             BlockingQueue<Runnable> workQueue,任务队列,被提交但尚未被执行的任务
             ThreadFactory threadFactory,表示生成线程池中工作线程的线程工厂,用于创建线程一般默认即可
             RejectedExecutionHandler handler拒绝策略,表示当队列满了并且工作线程大于等于线程池最大线程数(maximumPoolSize)时如何处理

死锁?

     死锁
         1 死锁介绍: 死锁就是线程1需要请求的资源被线程2持有, 而线程2想要获取的资源被线程1持有
             1 两个线程都不释放自己的资源, 但又无法获取所需的资源而进入阻塞的一种状态
             2 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者进程在运行过程中,请求和释放资源的顺序不当而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去
         2 死锁产生的条件
             1 有多把锁
             2 有多个线程
             3 有同步代码块嵌套, 即一个线程完成一个任务可能需要拿到多把锁
         3 死锁解决方法
             1 杀死一个或多个进程(最简单的方法)
             2 抢占: 即从一个线程中获取资源, 将其分配给其他线程, 但可能会导致一些问题
             3 回滚: 系统可以定期记录每个进程的状态, 在产生死锁时, 将所有内容回滚到最后一个检查点
                 1 然后重新启动, 但以不同的方式分配资源, 以免发生死锁
             4 按照顺序加锁
                 1 即如果有两把锁, 必须先获取第一把锁之后才能获取第二把锁
                 2 不能直接先获取第二把锁
             5 不要用synchronized无限等待, 可以使用ReentranLock实现超时等待
                 1 即拿到第一把锁之后,在一定时间后拿不到第二把锁,就释放已经拿到的锁
         4 活锁: 死锁的变体
             1 例子: 两个人在走廊上面对面相遇, 每个人一边走一边让对方通过,但最终却在没有任何进展的情况下左右摇摆, 因为他们总是以相同的方式移动同时
         5 只有一个锁, 如果这个锁是互斥锁时, 也可能会产生死锁
             如果同一个线程先后两次调用lock,在第二次调用时,由于锁已经被占用,该线程会挂起等待别的线程释放锁;
             然而锁正是被自己占用着的,该线程又被挂起而没有机会释放锁,。因此就永远处于挂起等待状态了

如何保证线程安全?

     如何保证线程安全?
         1 线程安全在三个方面体现
             1 原子性: 供互斥访问,同一时刻只能有一个线程对数据进行操作,(atomic,synchronized)
             2 可见性: 一个线程对主内存的修改可以及时地被其他线程看到,(synchronized,volatile)
             3 有序性: 一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序
         2 解决方法
             1 使用原子类
                 1 在Java.util.concurrent.atomic包下定义了一些对"变量"操作的"原子类"
                 2 原子类又称为乐观锁, cas机制 compare and swap
             2 使用 synchronized 关键字
             3 使用 Lock 锁
                 lock()获得锁
                 unlock()释放锁
 ​