一、为什么要有线程池
使用线程的时候就去创建一个线程,这样很方便,但是如果并发数量很多,每一个线程都是经过短暂的使用就结束,这样频繁的创建和销毁线程需要时间,就会降低系统的效率;
为了减少创建和销毁线程的次数,让每个线程可以多次使用,线程池可以使线程得以复用,在执行完一个任务之后不被销毁,而是可以继续执行其他任务,这样可以根据系统情况调整执行的线程数量,防止消耗过多内存。
优点:
- 降低资源消耗:通过重复利用已创建的线程来降低线程的创建和销毁所带来的的损耗;
- 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能直接执行;
- 提高线程的可管理性: 线程是稀缺资源,如果无限制地创建,会消耗系统资源并且降低系统的稳定性,线程池可以进行统一分配,调优和监控。
二、线程池的继承关系
Executor:java线程池的顶层接口,其定义了一个接收Runnable对象的方法executor();
ExecutorService:ExecutorService是Executor的子类,也是真正的线程池接口,提供了一些生命周期管理的方法,例如提交任务和关闭线程池等方法。调用submit方法提交任务还可以返回一个Future对象,利用该对象可以了解任务执行情况,获得任务的执行结果或取消任务;
ScheduledExecutorService:一个可定时调度任务的接口;
Executors:由于线程池的配置比较复杂,JavaSE中定义了Executors类,方便用来创建各种常用线程池的工具类,通过调用该工具类中的方法我们可以创建四种线程池;
单线程池(newSingleThreadExecutor)
固定数量的线程池(newFixedThreadPool)
可缓存线程池(newCachedThreadPool)
大小无限制的线程池(newScheduledThreadPool)
AbstractExecutorService:ExecutorService执行方法的默认实现;
ThreadPoolExecutor:线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象;
ScheduledThreadPoolExecutor:一个可定时调度任务的线程池,ScheduledExecutorService的实现;
存在的接口
1)Executor
public interface Executor {
void execute(Runnable command); //用来接受Runnable实例
}
execute(Runnable command) 方法接收一个Runnable实例,它用来执行一个任务,任务就是一个实现了Runnable接口的类;
- 参数:
command:是可以运行的任务 - 方法的异常抛出:
RejectedExecutionException – 不能接受执行此任务。
NullPointerException – 命令为 null
java.util.concurrent包下提供的Executor 实现了ExecutorService ,这是一个使用更广泛的接口。ThreadPoolExecutor 类提供一个可扩展的线程池实现。Executors类为这些 Executor 提供了便捷的工厂方法。
内存一致性效果:线程中将 Runnable 对象提交到 Executor 之前的操作happen-before其执行开始(可能在另一个线程中)。
2)ExecutorService
ExecutorService继承Executor接口,比Exector更加广泛,提供了一些生命周期管理的方法,以及可以跟踪一个或多个异步执行状况返回Future的方法
public interface ExecutorService extends Executor {
void shutdown(); //启动依次顺序关闭,执行以前提交的任务,但不接受新的任务,如果已经关闭,则调用没有其他作用。
List<Runnable> shutdownNow(); //试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表
boolean isShutdown(); //如果此执行程序已经关闭则返回true
boolean isTerminated(); //如果关闭后所有的任务都已经完成则返回true
//请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行。
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
<T> Future<T> submit(Callable<T> task); // 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future
<T> Future<T> submit(Runnable task, T result); // 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
Future<?> submit(Runnable task); // 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
// 执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
//执行给定的任务,当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的 Future 列表
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
//执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
//执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
3)ScheduledExecutorService:一个可以定时调度的接口
public interface ScheduledExecutorService extends ExecutorService {
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
三、ThreadPoolExecutor
1、继承关系
public class ThreadPoolExecutor extends AbstractExecutorService
2、参数
private final BlockingQueue<Runnable> workQueue; //存放任务的阻塞队列
private final ReentrantLock mainLock = new ReentrantLock(); //重入锁
private final HashSet<Worker> workers = new HashSet<Worker>(); //包含线程池中所有工作线程的集合
private final Condition termination = mainLock.newCondition(); //条件对象
private int largestPoolSize; //记录工作线程数量的最大值
private long completedTaskCount; //已经完成任务的计数器,仅在工作线程终止时更新(线程池处于Terminate状态)
private volatile ThreadFactory threadFactory; //线程工厂,用来创建线程,用户可以自定义
private volatile RejectedExecutionHandler handler; //(饱和策略)被拒绝的执行处理程序
private volatile long keepAliveTime; //保持活动时间
/*
* 为true则适用于非核心线程的相同的保持活动策略也同样适用于核心线程
* 为false则只会回收核心池外的空闲线程
*/
private volatile boolean allowCoreThreadTimeOut; //决定核心池中的空闲线程是否被在keepAliveTime时间后被回收
private volatile int corePoolSize; //核心线程池的大小
private volatile int maximumPoolSize; //线程池中可以运行的最大线程数
3、构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
在其构造函数中有七个非常重要的参数:
1)corePoolSize:核心池的大小,也就是线程池中会维持不被释放的线程数量,当有新任务提交时,如果运行的线程少于corePoolSize,即使其他辅助线程是空闲的,线程池也会创建新的线程去执行任务,当线程池中的线程数量大于corePoolSize小于maximumPoolSize,则仅当队满时才会创建线程;
2)maximumPoolSize:线程池中的最大线程数,指最多允许创建的线程个数;
ThreadPoolExecutor会根据corePoolSize和maximumPoolSize设置的边界来自动调整池的大小,如果设置的corePoolSize和maxmumPoolSize相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用setCorePoolSize(int)和setMaxmumPoolSize(int)对其边界进行动态更改。
3)keepAliveTime:保持活动时间,在默认情况下,在线程池中的线程数量大于corePoolSize时,多出的线程在空闲时间超过keepAliveTime时会被终止,直到线程池中的线程数不超过corePoolSize;这提供了当线程池处于非活动状态时减少资源消耗的方法,如果线程池后来变得更为活动,则可以创建新的线程。也可以使用方法setKeepAliveTime(long,java.util.concurrent.TimeOut)动态地更改此参数。但在调用allowCoreThreadTimeOut(boolean)方法后,只要keepAliveTime大于0,就算线程池中的线程数不大于corePoolSize,也会将这种超时策略用于核心线程,直到线程池中的线程数为0;
4)unit:keepAliveTime的单位,TimeUnit枚举类型,有7种取值
DAYS | HOURS | MINUTES | SECONDS | MILLISECONDS | MICROSECONDS | NANOSECONDS |
---|---|---|---|---|---|---|
天 | 小时 | 分钟 | 秒 | 毫秒 | 微秒 | 纳秒 |
5)workQueue:任务队列,当核心池中的线程都处于运行状态,后面提交的任务就会保存在这个阻塞队列里,可用于传输和保持提交的任务;
- 如果运行的线程 < corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。
- 如果运行的线程 >= corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
- 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
排队的策略及常用的阻塞队列
- 直接提交。工作队列的默认选项是 SynchronousQueue(一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,在静态工厂方法Executors.newCachedThreadPool中使用了这个队列),它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性;
- 无界队列。使用无界队列(例如不具有预定义容量的 LinkedBlockingQueue,在静态工厂方法Executors.newFixedThreadPool()中就使用了这个队列)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样创建的线程就不会超过 corePoolSize。(这样maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列,除了应用LinkedBlockingQueue外还可能使用到PriorityBlockingQueue,这是一个具有优先级的无限阻塞队列;
- 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞,那系统可能为超过许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
6)threadFactory:线程工厂,使用ThreadFactory来创建线程,如果没有特殊说明,在同一个 ThreadGroup (线程组)中都使用Executors.defaultThreadFactory()来创建线程,那这些线程具有相同的优先级和非守护进程状态,通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态等。如果从newThread方法返回的是null,则说明ThreadFactory未能创建线程,虽然执行程序可以继续运行,但不能执行任何任务。
7)handler:拒绝策略,定义任务被拒绝时会执行的处理程序;
有以下四种拒绝策略
ThreadPoolExecutor.AbortPolicy | ThreadPoolExecutor.DiscardPolicy | ThreadPoolExecutor.DiscardOldestPolicy | ThreadPoolExecutor.CallerRunsPolicy |
---|---|---|---|
直接抛出RejectedExecutionException异常 | 丢弃当前任务,不抛出异常 | 丢弃队列里最近的一个任务,并执行当前任务 | 由调用者所在线程处理该任务 |
四、线程池的实现原理:
在ThreadPoolExecutor类中,最核心的任务提交方法是execute()方法,虽然通过submit也可以提交任务,但是实际上submit方法里面最终调用的还是execute()方法
ThreadPoolExecutor执行execute()方法的流程:
- 线程池判断核心线程池里的线程是否都在执行任务
- 如果不是,则创建一个新的工作线程来执行任务;
- 如果核心线程池里的线程都在执行任务,则进入下个流程;
- 线程池判断工作队列是否已经满
- 如果工作队列没有满,则将新提交的任务存储在这个工作队列里;
- 如果工作队列满了,则进入下个流程;
- 线程池判断池中的线程是否都处于工作状态
- 如果没有,则创建一个新的工作线程来执行任务;
- 如果已经满了,则交给饱和策略来处理这个任务;
如果在上一个流程图中看的不是很清楚,下面这幅图相对来说更加直观:
整个过程有四步:
- 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(需要获取全局锁);
- 如果运行的线程等于或多于corePoolSize,则将任务加入workQueue;
- 如果阻塞队列已满,无法将任务加入,则创建新的线程来处理任务(需要获取全局锁);
- 如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,此时会调用到饱和策略来处理当前提交的任务;
ThreadPoolExecutor采取这样的设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁,在核心池中的线程数量到达上限时,几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁;
execute()方法的源码:
public void execute(Runnable command) {
if (command == null) {
throw new NullPointerException();
}
int c = ctl.get(); //获取当前线程池的状态和运行线程的数量
if (workerCountOf(c) < corePoolSize) { //第一步,在当前运行的线程数量<corePoolSize时
if (addWorker(command, true)) { //调用addWorker(command, true)第一次尝试创建核心线程
return; //线程创建成功则返回
}
c = ctl.get(); //若线程创建失败则说明此时的c已经被别的线程修改,重新获取线程池状态和线程运行数量
}
//第二步,尝试将当前任务存入阻塞队列中,而在线程池在shutDown及之后的状态时是不会接受新任务的
if (isRunning(c) && workQueue.offer(command)) { //所以此时要确保是在线程池处于running状态时将任务添加进工作队列
//再次获取当前线程池的状态和运行线程的数量
int recheck = ctl.get(); //改变有两种可能:1、线程池处于非运行状态 2、线程池中没有线程了
//符合下面两个条件判断之一,说明任务添加进工作队列失败
if (! isRunning(recheck) && remove(command)) { //如果线程池处于非运行状态则在工作队列中删除刚刚添加的任务
reject(command); //调用饱和策略处理该任务
} else if (workerCountOf(recheck) == 0) { //线程池中运行的线程数为0,在此时创建一个空的线程
addWorker(null, false);
}
//如果上面两个条件判断都不符合,则说明成功将任务添加进工作队列
} else if (!addWorker(command, false)) { //第三步,尝试调用addWorker(command, true)创建非核心线程来处理任务
reject(command); //第四步,创建失败,调用饱和策略处理该任务
}
}
在execute()方法中有两次调用addWorker()方法来创建线程,分别是第一次调用addWorker(command, true)和第二次调用addWorker(command, true);
private boolean addWorker(Runnable firstTask, boolean core) {
//CAS操作,目的是让线程数量+1
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//条件判断,@#¥%%…#&*,无法组织语言 -_-,不符合时返回false
//看这个条件 rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()
//在线程池处于shutDown状态时还会继续处理工作队列中的任务
//就是说,在线程池是shutDown状态时,工作队列不为空,并且传进来的任务firstTask为null,可以创建一个空的新线程
//为什么要创建没有任务的空线程,因为创建它的目的还是要去到工作队列中拿任务去执行,
//而如果这时候工作队列是空的,那就是说已经没有需要执行的任务那也就不需要再创建这个线程了
//那就是说除了这种情况下的任何非运行状态,在这里尝试创建线程都会失败返回false
if (rs >= SHUTDOWN && //线程池处于非运行状态
! (rs == SHUTDOWN && //不再接受新的任务
firstTask == null && //传入任务为null
! workQueue.isEmpty())) //并且阻塞队列不为空
return false;
for (;;) { //进入这个for循环就说明当前线程池是可以创建线程的
int wc = workerCountOf(c); //当前运行的线程数量
if (wc >= CAPACITY || //wc>=容量或者wc>=核心线程数量或者最大线程数量时返回false
//用哪个参数取决于调用addWorker方法时传入的布尔类型的参数core
wc >= (core ? corePoolSize : maximumPoolSize)) //true用核心线程数量比较,false用最大线程数量进行比较
return false;
if (compareAndIncrementWorkerCount(c)) //线程数量+1
break retry; //跳出retry
c = ctl.get(); // 获取线程数量和线程池状态
if (runStateOf(c) != rs) //判断状态码是否改变,被改变则重新从retry处开始执行,也就是外层for循环
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
//创建一个worker实例
boolean workerStarted = false; //线程是否启动
boolean workerAdded = false; //线程是否添加成功
Worker w = null;
try {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask); //创建worker实例
final Thread t = w.thread; //当前线程任务
if (t != null) { //线程不为空时
mainLock.lock(); //获取锁
try {
int c = ctl.get(); //获取线程池状态和线程数量
int rs = runStateOf(c); //获取线程池的状态
if (rs < SHUTDOWN || //线程池处于running状态
(rs == SHUTDOWN && firstTask == null)) { //或者线处于shutdown状态并且创建的是空线程
if (t.isAlive()) //如果线程已经启动则抛出异常,因为已经启动的线程不用添加
throw new IllegalThreadStateException();
workers.add(w); //将worker加入到工作线程的集合中
int s = workers.size(); //修改线程池中线程的数量
if (s > largestPoolSize) //若s>largestPoolSize,更新工作线程数量的最大值
largestPoolSize = s;
workerAdded = true; //将线程添加成功的标志置为true
}
} finally {
mainLock.unlock(); //释放锁
}
if (workerAdded) { //添加成功则启动线程
t.start();
workerStarted = true; //将线程已被启动的标志置为true
}
}
} finally {
if (! workerStarted) //若线程没有启动成功
addWorkerFailed(w); //从线程池中删除线程并且修改线程池中线程的数量
}
return workerStarted; //线程启动才算添加成功
}
可能到这里,本来是要创建线程却又跟Worker傻傻分不清楚,其实这个线程是封装在一个Worker实例中的。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
final Thread thread;
Runnable firstTask;
volatile long completedTasks;
Worker(Runnable firstTask) { //在这里创建一个线程,newThread方法中传入的是Worker自身的runnable
setState(-1); //设置当前AQS状态,禁止中断,直到启动
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() { //实现run方法,而它实际上用的是ThreadPoolExecutor中的runWorker方法
runWorker(this);
}
protected boolean isHeldExclusively() {
return getState() != 0;
}
//尝试获取锁
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) { //获取锁,成功则将获取锁的线程设置为当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false; //失败返回false
}
//取消获取锁状态
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
//设置中断状态
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
runWorker() 方法是ThreadPoolExecutor类中的的方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread(); //拿到当前线程
Runnable task = w.firstTask; //拿到线程中的任务
w.firstTask = null; //将线程的任务置为null
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//当task不为null,或者工作队列不为null时,worker会一直执行任务
while (task != null || (task = getTask()) != null) { //getTask()不断从阻塞队列提取任务交给线程。
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) || //所有任务执行完毕或者被强行中止
(Thread.interrupted() && //测试线程是否被中断,连续连续两次调用此方法则会返回false
runStateAtLeast(ctl.get(), STOP))) && //所有任务执行完毕或者被强行终止
!wt.isInterrupted()) //wt没有被中断
wt.interrupt(); //中断wt
try {
beforeExecute(wt, task); //回调方法,在子类中具体实现
Throwable thrown = null;
try {
task.run(); //执行任务
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown); //回调方法,在子类中具体实现
}
} finally {
task = null;//任务task置为空,如果进入下一个循环可以继续取任务
w.completedTasks++;//完成任务数量+1
w.unlock();
}
}
completedAbruptly = false; //说明不是用户任务异常引起的
} finally {
processWorkerExit(w, completedAbruptly);
}
}
runWorker()中有一个很重要的getTask() 方法
getTask()不断从阻塞队列提取任务交给线程,控制在线程超过空闲时间时回收线程;
private Runnable getTask() {
boolean timedOut = false; //获取任务是否超时,将其初始化为false
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//线程池为shutDown状态时workQueue为空,或者是>stop状态
//或者说线程池不在运行状态或者处于shutDown状态时工作队列为空
//当前线程不会再执行任务,直接回收该线程
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount(); //CAS操作将线程数量-1,
return null;
}
boolean timed; //表示当前线程在空闲时是否应该回收
for (;;) {
int wc = workerCountOf(c); //当前运行的线程数量
//如果allowCoreThreadTimeOut为true或者当前线程数量大于核心线程数时,timed为true,说明需要超时回收,反之则为false
//如果线程数量小于核心线程数则不需要
timed = allowCoreThreadTimeOut || wc > corePoolSize;
if (wc <= maximumPoolSize && ! (timedOut && timed)) //如果线程数小于最大线程数,并且不允许超时回收并且未超时
break; //跳出内层for循环,去workQueuez中拿取任务
//不满足上一个条件判断说明已经超时了,则工作队列中没有任务了
if (compareAndDecrementWorkerCount(c)) //回收当前线程,线程数量-1,成功则返回true
return null; //返回null,回收该线程
c = ctl.get(); //如果在上面CAS修改线程数量失败,重新获取ctl
if (runStateOf(c) != rs) //判断线程池当前状态与rs不相等则重新执行retry
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
try { //如果线程池需要超时回收,调用poll方法(返回结果),否则进行take操作(线程阻塞,直到获取任务)
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null) //获取到任务则返回
return r;
timedOut = true; //否则将timedOut置为true,进入下一个循环
} catch (InterruptedException retry) {
timedOut = false;
}
}
}