面试之 Java 并发编程(二)

7、为什么使用Executor 框架?

 什么是Executor框架?

我们知道线程池就是线程的集合,线程池集中管理线程,以实现线程的重用,降低资源消耗,提高响应速度等。线程用于执行异步任务,单个的线程既是工作单元也是执行机制,从JDK1.5开始,为了把工作单元与执行机制分离开,Executor框架诞生了,他是一个用于统一创建与运行的接口。Executor框架实现的就是线程池的功能。

每次执行任务创建线程 new Thread()比较消耗性能, 创建一个线程是比较耗时、耗资源 的。 调用 new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制的创建, 线程之 间的相互竞争会导致过多占用系统资源而导致系统瘫痪, 还有线程之间的频繁交替也会消 耗很多系统资源。 接使用 new Thread() 启动的线程不利于扩展, 比如定时执行、定期执行、定时定期执 行、线程中断等都不便实现。

使用 Executor 线程池框架的优点

1、能复用已存在并空闲的线程从而减少线程对象的创建从而减少了消亡线程的开销。

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

3、框架中已经有定时、定期、单线程、并发数控制等功能。

Executor框架包括3大部分:

(1)任务。也就是工作单元,包括被执行任务需要实现的接口:Runnable接口或者Callable接口;

(2)任务的执行。也就是把任务分派给多个线程的执行机制,包括Executor接口及继承自Executor接口的ExecutorService接口。

(3)异步计算的结果。包括Future接口及实现了Future接口的FutureTask类。

 

8、在Java 中Executor 和Executors 的区别?

Executors 工具类的不同方法按照我们的需求创建了不同的线程池, 来满足业务的需 求。 Executor 接口对象能执行我们的线程任务。 ExecutorService 接口继承了 Executor 接口并进行了扩展, 提供了更多的方法我们能获 得任务执行的状态并且可以获取任务的返回值。 使用 ThreadPoolExecutor 可以创建自定义线程池。Future 表示异步计算的结果,他提供了检查计算是否完成的方法,以等待计算的完成, 并可以使用 get()方法获取计算的结果

 

9、如何在Windows 和 Linux 上查找哪个线程使用的 CPU 时 间最长?

参考: https://blog.csdn.net/kfyong/article/details/108394456

 

10、什么是原子操作?在 Java Concurrency API 中有哪些原 子类(atomic classes)?

原子操作( atomic operation) 意为” 不可被中断的一个或一系列操作” 。 处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作。 在 Java 中可以通过锁和循环 CAS 的方式来实现原子操作。 CAS 操作— — Compare & Set,或是 Compare & Swap,现在几乎所有的 CPU 指令都支持 CAS的原 子操作。 原 子 操 作 是 指 一 个 不 受 其 他 操 作 影 响 的 操 作 任 务 单 元 。 原 子 操 作 是 在 多 线 程 环 境 下避免数据不一致必须的手段。 int++并不是一个原子操作,所以当一个线程读取它的值并加 1 时,另外一个线程有可能 会读到之前的值, 这就会引发错误。 为了解决这个问题,必须保证增加操作是原子的,在 JDK1.5 之前我们可以使用同步技术 来做到这一点。到 JDK1.5, java.util.concurrent.atomic 包提供了 int 和long 类型的原 子包装类, 它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。

java.util.concurrent 这个包里面提供了一组原子类。其基本的特性就是在多线程环境下, 当有多个线程同时执行这些类的实例包含的方法时, 具有排他性, 即当某个线程进入方 法, 执行其中的指令时, 不会被其他线程打断, 而别的线程就像自旋锁一样, 一直等到 该方法执行完成, 才由 JVM 从等待队列中选择一个另一个线程进入, 这只是一种逻辑上 的理解。 原子类: AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference 原子 数组: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray 原子属性 更新器: AtomicLongFieldUpdater, AtomicIntegerFieldUpdater, AtomicReferenceFieldUpdater 解决 ABA 问题的原子类: AtomicMarkableReference( 通过引入一个 boolean 来反 映中间有没有变过), AtomicStampedReference( 通过引入一个 int 来累加来反映中 间有没有变过)

11、Java Concurrency API 中的Lock 接口(Lock interface) 是什么?对比同步它有什么优势?

Lock 接口比同步方法和同步块提供了更具扩展性的锁操作。 他们允许更灵活的结构, 可以具有完全不同的性质, 并且可以支持多个相关类的条件对 象。 它的优势有: 可以使锁更公平 可以使线程在等待锁的时候响应中断 可以让线程尝试获取锁, 并在无法获取锁的时候立即返回或者等待一段时间可以在不 同的范围, 以不同的顺序获取和释放锁整体上来说 Lock 是 synchronized 的扩展版, Lock 提供了无条件的、可轮询的(tryLock 方法)、定时的(tryLock 带参方法)、可中断的(lockInterruptibly)、可多条件队列的 (newCondition 方法)锁操作。另外 Lock 的实现类基本都支持非公平锁(默认)和公平锁, synchronized 只支持非公平锁, 当然, 在大部分情况下, 非公平锁是高效的选择。

12、什么是 Executors 框架?

Executor 框架是一个根据一组执行策略调用, 调度, 执行和控制的异步任务的框架。 无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的 解决方案, 因为可以限制线程的数量并且可以回收再利用这些线程。利用 Executors 框架可以非常方便的创建一个线程池。

13、什么是阻塞队列?阻塞队列的实现原理是什么?如何使用 阻塞队列来实现生产者-消费者模型?

 阻塞队列( BlockingQueue) 是一个支持两个附加操作的队列。 这两个附加的操作是: 在队列为空时, 获取元素的线程会等待队列变为非空。当队列满 时, 存储元素的线程会等待队列可用。 阻塞队列常用于生产者和消费者的场景, 生产者是往队列里添加元素的线程, 消费者是 从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器, 而消费者也只从容器里拿 元素。 JDK7 提供了 7 个阻塞队列。分别是:

ArrayBlockingQueue : 一个由数组结构组成的有界阻塞队列。 LinkedBlockingQueue : 一个由链表结构组成的有界阻塞队列。 PriorityBlockingQueue : 一个支持优先级排序的无界阻塞队列。 DelayQueue: 一个使用优先级队列实现的无界阻塞队列。 SynchronousQueue: 一个不存储元素的阻塞队列。 LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。 LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。

Java 5 之前实现同步存取时, 可以使用普通的一个集合, 然后在使用线程的协作和线程 同步可以实现生产者, 消费者模式, 主要的技术就是用好, wait ,notify,notifyAll,sychronized 这些关键字。而在 java 5 之后, 可以使用阻塞队列 来实现, 此方式大大简少了代码量, 使得多线程编程更加容易, 安全方面也有保障。 BlockingQueue 接口是 Queue 的子接口, 它的主要用途并不是作为容器, 而是作为线 程同步的的工具, 因此他具有一个很明显的特性, 当生产者线程试图向 BlockingQueue 放入元素时, 如果队列已满, 则线程被阻塞, 当消费者线程试图从中取 出一个元素时, 如果队列为空, 则该线程会被阻塞, 正是因为它所具有这个特性, 所以 在程序中多个线程交替向 BlockingQueue 中放入元素, 取出元素, 它可以很好的控制线 程之间的通信。 阻塞队列使用最经典的场景就是 socket 客户端数据的读取和解析,读取数据的线程不断 将数据放入队列, 然后解析线程不断从队列取数据解析。

14、什么是 Callable 和Future?

Callable 接口类似于 Runnable,从名字就可以看出来了,但是 Runnable 不会返回结 果, 并且无法抛出返回结果的异常, 而 Callable 功能更强大一些, 被线程执行后,可以 返回值,这个返回值可以被 Future 拿到,也就是说,Future 可以拿到异步执行任务的返 回值。 可以认为是带有回调的 Runnable。

Future 接口表示异步任务,是还没有完成的任务给出的未来结果。所以说 Callable 用于 产生结果, Future 用于获取结果。

15、什么是 FutureTask?

使用 ExecutorService 启动任务。 在 Java 并发程序中 FutureTask 表示一个可以取消的异步运算。它有启动和取消运算、查 询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算 尚未完成 get 方法将会阻塞。一个 FutureTask 对象可以对调用了 Callable 和 Runnable 的对象进行包装,由于 FutureTask 也是调用了 Runnable 接口所以它可以提交给 Executor 来执行。

16、什么是并发容器的实现?

何为同步容器:可以简单地理解为通过 synchronized 来实现同步的容器,如果有 多个线程调用同步容器的方法, 它们将会串行执行。比如 Vector, Hashtable, 以及 Collections.synchronizedSet,synchronizedList 等方法返回的容器。 可以通过查看 Vector, Hashtable 等这些同步容器的实现代码, 可以看到这些容器实现 线程安全的方式就是将它们的状态封装起来, 并在需要同步的方法上加上关键字 synchronized。 并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性, 例如在 ConcurrentHashMap 中采用了一种粒度更细的加锁机制, 可以称为分段锁, 在这种锁 机制下, 允许任意数量的读线程并发地访问 map, 并且执行读操作的线程和写操作的线 程也可以并发的访问 map, 同时允许一定数量的写操作线程并发地修改 map, 所以它 可以在并发环境下实现更高的吞吐量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值