上海-拼多多电商部二面(2)

6 、线程的状态流转图

线程的生命周期及五种基本状态:

上海-拼多多电商部二面(2)

7 、Java 线程具有五种基本状态

(1)新建状态(New):

当线程对象对创建后,即进入了新建状态,如:Thread t= new MyThread();

(2)就绪状态(Runnable):

当调用线程对象的 start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待 CPU 调度执行,并不是说执行了 t.start()此线程立即就会执行;

(3)运行状态(Running):

当 CPU 开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。

注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

(4)阻塞状态(Blocked):

处于运行状态中的线程由于某种原因,暂时放弃对 CPU 的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被 CPU 调用以进入到运行状态。

根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1)等待阻塞:运行状态中的线程执行 wait()方法,使本线程进入到等待阻塞状态;

2)同步阻塞:线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3)其他阻塞:通过调用线程的 sleep()或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。

4)死亡状态(Dead):

线程执行完了或者因异常退出了 run()方法,该线程结束生命周期。

8 、什么是线程池?有哪几种创建方式?

线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程的时候就可以考虑使用线程池来提升系统的性能。

java 提供了一个 java.util.concurrent.Executor 接口的实现用于创建线程池。

9 、四种线程池的创建:

(1)newCachedThreadPool 创建一个可缓存线程池。

(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数。

(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务。

10、线程池的优点

(1)重用存在的线程,减少对象创建销毁的开销。

(2)可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

(3)提供定时执行、定期执行、单线程、并发数控制等功能。

11 、常用的并发工具类有哪些?

(1)CountDownLatch

(2)CyclicBarrier

(3)Semaphore

(4)Exchanger

12 、CyclicBarrier 和 和 CountDownLatch 的区别?

(1)CountDownLatch 简单的说就是一个线程等待,直到他所等待的其他线程都执行完成并且调用 countDown()方法发出通知后,当前线程才可以继续执行。

(2)cyclicBarrier 是所有线程都进行等待,直到所有线程都准备好进入 await()方法之后,所有线程同时开始执行!

(3)CountDownLatch 的计数器只能使用一次。而 CyclicBarrier 的计数器可以使用 reset()方法重置。所以 CyclicBarrier 能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。

(4)CyclicBarrier 还提供其他有用的方法,比如 getNumberWaiting 方法可以获得CyclicBarrier 阻塞的线程数量。isBroken 方法用来知道阻塞的线程是否被中断。如果被中断返回 true,否则返回 false。

13 、synchronized 的作用?

在 Java 中,synchronized 关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized 代码段不被多个线程同时执行。synchronized 既可以加在一段代码上,也可以加在方法上。

14 、volatile 关键字的作用

对于可见性,Java 提供了 volatile 关键字来保证可见性。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。从实践角度而言,volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详细的可以参见 java.util.concurrent.atomic 包下的类,比如 AtomicInteger。

15、是 什么是 CAS

CAS 是 Compare And Swap 的缩写,即我们所说的比较交换。

CAS 是一种基于锁的操作,而且是乐观锁。在 java 中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加 version 来获取数据,性能较悲观锁有很大的提高。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和 A 的值是一样的,那么就将内存里面的值更新成 B。CAS 是通过无限循环来获取数据的,若果在第一轮循环中,a 线程获取地址里面的值被 b线程修改了,那么a线程需要自旋,到下次循环才有机会执行。java.util.concurrent.atomic 包下的类大多是使用CAS操作来实现的(AtomicInteger、AtomicBoolean,AtomicLong)。

16 、CAS 的问题

(1)CAS 容易造成 ABA 问题

一个线程 a 将数值改成了 b,接着又改成了 a,此时 CAS 认为是没有变化,其实是已经变化过了,而这个问题的解决方案可以使用版本号标识,每操作一次 version 加 1。在 java5中,已经提供了 AtomicStampedReference 来解决问题。

(2)不能保证代码块的原子性

CAS 机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证 3 个变量共同进行原子性的更新,就不得不使用 synchronized 了。

(3)CAS 造成 CPU 利用率增加

之前说过了 CAS 里面是一个循环判断的过程,如果线程一直没有获取到状态,cpu 资源会一直被占用。

上一篇:源码分析:CyclicBarrier 之循环栅栏


下一篇:Java中使用CyclicBarrier