问题1 什么是executor框架
Executor and ExecutorService用于以下场景:
- 创建线程
- 启动线程
- 管理整个线程的生命周期
Executor创建线程池并管理线程池中线程的生命周期,在executor框架中,Executor接口和ExecutorService接口被频繁使用。Executor接口定义了execute()方法用于执行命令。ExecutorService接口继承Executor。
Executor提供了管理终止或产生Future追踪过程或异步任务的方法,更多内容请阅读Executor and ExecutorService framework in java。
问题2 executor框架中execute()和submit()的区别
execute()方法 | submit()方法 |
---|---|
execute()方法定义在Executor接口中 | submit()方法定义在ExecutorService接口中 |
用于执行runnable任务 | 用于执行runnable任务或callable任务,提交callable任务返回future对象,通过future.get() 获取任务的结果 |
方法类型只有一种void execute(Runnable task) | submit方法有3种形式:<T> Future<T> submit(Callable<T> task) <T> Future<T> submit(Runnable task, T result) Future<?> submit(Runnable task) |
问题3 什么是Semaphore?
Semaphore通过许可控制共享资源的访问:
- 如果许可permits大于0,semaphore允许访问共享资源;
- 如果许可permits小等于0,semaphore不允许访问共享资源;
其中许可有点类似计数器,用来允许访问共享资源,为此,为了访问该资源,线程必须从semaphore那里授权许可。
Semaphore有两个构造函数:
1. Semaphore(int permits)
:permits是初始的可用许可,这个值有可能是负值,表示此刻等待获取共享资源的线程数。如果permits=1,表示只有一个线程正在使用共享资源。
2. Semaphore(int permits, boolean fair)
:设置fair,能确保等待线程按请求顺序被授权。
acquire( )方法有两种形式:
1. void acquire( ) throws InterruptedException
:获取一个可用许可,并且可用许可减1。
2. void acquire(int permits) throws InterruptedException
:获取permits个可用许可,同时可用许可减permits。如果permits个许可不可用,那么当前线程处于休眠状态,直到发生如下事情:
- 其他线程调用release()
方法,可用许可大等于permits;
- 或者其他线程中断该线程。
release( )方法有两种形式:
1. void release( )
:释放许可,并且许可加1。
2. void release(int permits)
:释放permits个许可,并且可用许可增加permits个。
有关Semaphor请阅读Semaphore in java。
问题4 利用Semaphore实现生产者消费者模式
请阅读:Semaphore used for implementing Producer Consumer pattern
问题5 实现自己的Semaphore
请阅读:Implementation of custom/own Semaphore in java
问题6 Java 7中atomic类的意义
Java在java.util.concurrent.atomic中提供了其他的同步类,这些类有AtomicInteger
、AtomicLong
、AtomicBoolean
等,相应的方法有get()
,set()
,getAndSet()
,compareAndSet( )
,decrementAndGet( )
等。在多线程环境下,这些类不需要显示地进行同步,因为它们是线程安全的。
atomic的更多内容请参考Atomic operations in java。
问题7 Future和Callable之间的关联?
Future<V>
接口提供的方法用于返回计算的结果,计算未完成会一直等待,或者取消计算任务。
Callable<V>
接口提供了计算结果的方法,并返回计算结果,或者抛出异常。任何实现Callable接口的类必须重写call()方法。
向Executor提交Callable对象返回的对象类型是Future,如:
Future<Double> futureDouble=executor.submit(new SquareDoubleCallable(2.2));
更多内容请阅读Executor and ExecutorService framework in java。
问题8 java.util.concurrent.Callable和java.lang.Runnable不同?
实现Callable接口的类必须重写call()方法,call()方法返回计算结果或者无法执行抛出异常;
实现Runnable接口的类必须重写run()方法,run()方法不会返回计算结果,也不抛出检查异常。
问题9 CountDownLatch
CountDownLatch适用于当前线程等待一个或多个线程完成特定的操作。CountDownLatch(int count) 初始化需要确定在latch释放之前发生事件的个数。事件每次发生count减1,一旦count减为0,latch被释放。
CountDownLatch的await()方法有两种形式:
- void await( ) throws InterruptedException
引起当前线程等待,直到latch count减为0,或者当前线程被中断。
- boolean await(long timeout, TimeUnit unit)
引起当前线程等待,直到latch count减为0,或者当前线程被中断,或者超时。
countDown()方法用来减少latch count计数。count减为0,所有等待线程被释放。
了解更多请阅读CountDownLatch in java。
问题10 CountDownLatch的使用场景
问题11 如何实现CountDownLatch?
请阅读Implementation of custom/own CountDownLatch in java。
问题12 CyclicBarrier
CyclicBarrier用于一个或多个线程完成都完成特定的操作才触发一个事件。2个或者多个线程彼此等待直到到达共同的barrier point。当所有的线程到达共同的barrier point(即所有的线程调用await()方法):
- 所有的等待线程被释放;
- 事件被触发。
CyclicBarrier的构造函数:
- CyclicBarrier(int parties)
新的CyclicBarrier被创建,当parties个等待线程到达共同的barrier point,等待的线程被释放。
- CyclicBarrier(int parties, Runnable barrierAction)
新的CyclicBarrier被创建,当parties个等待线程到达共同的barrier point,等待的线程被释放,barrierAction事件被触发。
CyclicBarrier的await()方法:
- int await() throws InterruptedException, BrokenBarrierException
- int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException
当前的线程如果不是最后到达的(即调用await()方法),它会等待直到发生如下事情:
1. 最后一个线程调用await()方法
2. 其他线程中断了当前线程
3. 其他线程中断了其中的一个等待线程
4. 等待barrier的过程中一些线程超时
5. 在这个cyclicBarrier上的线程调用了reset()方法
6. 经过了timeout时间
更多内容请阅读CyclicBarrier in java。
问题13 为什么CyclicBarrier能够循环?
barrier之所以能够循环,是因为CyclicBarrier能够重用,当所有的等待线程释放,或者事件已被触发。
问题14 在实际场景中什么时候使用CyclicBarrier?
举例:有10个好朋友打算去地点A野餐(朋友是线程,地点A是共同的barrier point),他们决定玩某个游戏当所有的人员到达地点A(游戏是一个事件)。所以10个朋友必须等到他们都到齐才能进行游戏。
问题15 如何实现CyclicBarrier?
请参考Implementation of custom/own CyclicBarrier in java。
问题16 CyclicBarrier和CountDownLatch之间的异同
- CyclicBarrier和CountDownLatch有点相似,是因为它们要等待特定数量的线程到达某个点,然后使count或者parties等于0。但是CountDownLatch完成等待,线程必须调用countDown()方法;而CyclicBarrier完成等待,线程必须调用await()方法。
- 它们的构造函数区别:
CountDownLatch(int count) :count确定latch释放之前发生的事件个数;
CyclicBarrier(int parties):parties个线程相互等待到达共同的barrier point,当所有的线程到达这个共同的barrier point,parties个等待线程被释放。 - CyclicBarrier可以重复等待,而CountDownLatch不能,即一旦count减为0,cyclicBarrier可重复使用。
- CyclicBarrier可以触发事件。
问题17 Phaser是什么?
Phaser在功能上和CyclicBarrier和CountDownLatch有点相似,但是比它俩更加灵活。
Phaser为我们提供了更加灵活的注册和取消注册。对于注册,可以使用下面的方式:
- 构造函数
- int register()
- bulkRegister()
对于取消注册,可以使用下面的方式:
- arriveAndDeregister()
可以使用getPhase()方法返回当前的phase个数,isTerminated()返回phaser时候结束。
更多内容请阅读Phaser in java。
问题18 Phaser和CyclicBarrier之间的异同
CyclicBarrier和Phaser都可以重复等待。在CyclicBarrier中,在构造函数中注册,但是Phaser提供了在任何时候注册和取消注册的方法。
问题19 Phaser的arrive()方法和arriveAndAwaitAdvance() 的区别
Phaser的arrive()方法不会引起当前线程等待其他线程完成当前的phase,即当前线程可以立刻进入下一个phase,无须等待其他注册线程完成当前的phase。
而arriveAndAwaitAdvance()方法会让当前线程去等待其他注册线程完成当前的phase,即只有当其他的所有线程完成当前的phase,当前线程才可以进行下一个phase。
问题20 phaser什么时候结束
调用Phaser的arriveAndDeregister() 方法会让注册的线程数变为0。当onAdvance()方法返回true,终止也会被触发。
问题21 如何控制phase的个数
问题22 Phaser的使用场景
软件开发管理按如下phase进行:
1. 需求收集;
2. 软件开发;
3. 测试;
第二个phase不会开始直到第一个phase完成,第三个phase也不会开始直到第二个phase完成。
问题23 每次phaser注册的最大数量
每次phaser注册的数量为65535,如果想注册更多的线程,将会抛出IllegalStateException。
问题24 Exchanger
Exchanger能够使两个线程交换彼此的数据,它能够非常容易地实现生产者消费者模式,生产者和消费者线程交换彼此的数据。
exchange(V x)方法能够使两个线程交换彼此的数据,如果当前线程是第一个调用exchange()方法,那它会等待直到其他线程调用exchange()方法,或者其他线程中断当前线程。如果其他线程已经调用了exchange()方法,那么它会恢复执行,等待线程恢复并接受当前线程的数据,或者当前线程收到其他等待线程的数据,立刻返回。
更多内容请阅读Exchanger in java。
问题25 用Exchanger实现生产者消费者模式
请参考Read program to implement Producer Consumer pattern using Exchanger。
问题26 使用BlockingQueue解决生产者消费者模式
请参考BlockingQueue is a interface and we will use its implementation class LinkedBlockingQueue。
问题27 使用BlockingQueue解决生产者消费者模式
请参考Producer Consumer pattern using Custom implementation of LinkedBlockingQueue interface。
问题28 java Lock
java.util.concurrent.locks.Locks是一个接口,它的实现类提供了更实用的锁操作。lcok有助于控制多线程共享资源的访问,每次只有一个线程获取锁,并且访问共享资源。如果第二个线程试图访问锁上的共享资源,当这个锁被另一个线程获取,那么第二个线程处于等待,直到锁被释放。这样就可以实现同步和避免竞争。
更多内容请阅读locks and ReEntrantLocks in java。
问题29 Lcok 接口中的关键方法
void lock()
如果锁没有被其他线程占用,获取锁。设置锁计数为1;如果当前线程已经占用了锁,那么说计数器加1;如果锁被其他线程占用,那么当前线程等待直到锁被释放。
void unLock()
如果当前线程占用了锁,那么锁计数器减1;如果锁计数器减为0,那么锁被释放;如果锁计数器大于0,那么锁没有被释放;如果当前线程没占用锁,那么抛出IllegalMonitorStateException异常。
boolean tryLock()
获取锁如果没有被其他线程占用,并返回true,设置锁计数器设置为1。如果当前线程已经占有了锁,那么返回true,并锁计数器加1.如果锁被其他线程占用,返回false。
boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException
获取锁如果没有被其他线程占用,返回true,设置锁计数器为1。如果当前线程已经占有了锁,返回true,并且锁计数器加1。如果锁被其他线程占用,那么当前线程等待直到发生如下:1、另一个线程释放锁,当前线程获取锁;2、其他线程中断了当前线程;3、设定的时间超时。
Condition newCondition()
返回Condition实例,被用于锁接口。Condition实例类似于wait(), notify(), notifyAll()方法。
更多请阅读locks in java。