线程池
1 为什么要使用线程池?
一个线程完成一项任务所需时间为:创建线程时间(T1),在线程中执行任务的时间(T2),销毁线程时间(T3)。
线程池技术正是关注如何缩短或调整T1、T3时间的技术,从而提高程序的性能。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目。
系统启动一个新线程的成本是比较高的,因为涉及与操作系统的交互,在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,优先考虑使用线程池。
线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的Run方法。
JDK1.5以前,开发者必须手动实现自己的线程池,JDK1.5开始,Java已经实现且支持线程池
2 线程池的内部核心组成部分
一个线程池包括以下四个基本组成部分:
1. 线程池管理器(ThreadPool):用于创建并管理线程池,包括:创建线程池、销毁线程池和添加新任务;
2. 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4. 任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
3 ThreadPoolExecutor类
java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。
在ThreadPoolExecutor类中提供了四个构造方法:
从上面的代码可以得知,ThreadPoolExecutor继承了AbstractExecutorService(实现ExecutorService接口)类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。
下面解释下一下构造器中各个参数的含义:
corePoolSize:核心池的大小,在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把新加入的任务放到缓存队列当中;
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(true)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue:一个阻塞队列,用来存储等待执行的任务,一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。
threadFactory:线程工厂,主要用来创建线程;
handler:表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
在java doc中,并不提倡我们直接使用new ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池。
3 线程池的创建
线程池通过java.util.concurrent.Executors工厂类来创建。该工厂类包含以下几个静态工厂方法来创建:
1. newCachedThreadPool():创建一个具有缓存功能的线程池,当来了任务就创建线程运行,这些线程将会被缓存在线程池中。返回一个ExecutorService接口对象,代表一个线程池(内部代码实际创建的对象是ThreadPoolExecutor)。当线程空闲超过60秒,就销毁线程。
2. newFixedThreadPool(int nThreads):创建一个可以重用的、具有固定线程数的线程池。返回一个ExecutorService对象,代表一个线程池(内部代码实际创建的对象是ThreadPoolExecutor)。
3. newSingleThreadExecutor():创建一个只有单线程的线程池,它相当于newFixedThreadPool(1)。返回一个ExecutorService对象,代表一个线程池(内部代码实际创建的对象是ThreadPoolExecutor)。
4. newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,返回ScheduledExecutorService对象。它可以在指定延迟后执行线程任务(内部代码实际创建的对象是ScheduledThreadPoolExecutor)。
5. newSingleThreadScheduledExecutor():创建只有一条线程的线程池,它相当于newScheduledThreadPool(1)。它可以在指定延迟后执行线程任务。(内部代码实际创建的对象是DelegatedScheduledExecutorService,该类实际上是对ScheduledThreadPoolExecutor进行了包装)。
4 ExecutorService接口
代表一个线程池,只要线程池中有空闲线程立即执行线程任务,程序只要将一个Runnable对象或Callable对象提交给该线程池即可,该线程池就会尽快执行该任务。
1. execute() 这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
2. Future<?> submit(Runnable task):将一个Runnable对象提交给指定的线程池,线程池将在有空闲线程时执行Runnable对象代表的任务,其中Future对象代表Runnable任务的返回值,但run方法没有返回值,所有Future对象在run方法执行结束以后返回null,但可以调用Future的isDone等方法来获得Runnable对象的执行状态。
3. <T> Future<T> submit(Runnable task, T result):result显示指定线程执行结束后的返回值,所有Future对象将在run方法执行结束后返回result。 通过Future.get();该方法会阻塞线程等待该Runnable执行结束。
5 ThreadPoolExecutor 线程池的线程的关闭
shutdown():启动线程池的关闭序列,执行之后不再接受新任务,但会将之前所有已提交任务执行完毕,当线程池中的(所有任务)(包含阻塞队列的线程)都执行完毕后,线程池中的线程都会死亡。线程池也终止了...
shutdownNow():该方法会试图请求中断正在执行的线程(别忘了对线程的睡眠的终端异常加以处理),停止处理在等待的任务,并返回等待执行的任务列表。
newFixedThreadPool创建线程池示例代码:
ScheduledThreadPoolExecutor创建线程池示例代码:
线程池的执行(提交任务)示例代码:
1 为什么要使用线程池?
一个线程完成一项任务所需时间为:创建线程时间(T1),在线程中执行任务的时间(T2),销毁线程时间(T3)。
线程池技术正是关注如何缩短或调整T1、T3时间的技术,从而提高程序的性能。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目。
系统启动一个新线程的成本是比较高的,因为涉及与操作系统的交互,在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,优先考虑使用线程池。
线程池在系统启动时即创建大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的Run方法。
JDK1.5以前,开发者必须手动实现自己的线程池,JDK1.5开始,Java已经实现且支持线程池
2 线程池的内部核心组成部分
一个线程池包括以下四个基本组成部分:
1. 线程池管理器(ThreadPool):用于创建并管理线程池,包括:创建线程池、销毁线程池和添加新任务;
2. 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4. 任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
3 ThreadPoolExecutor类
java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。
在ThreadPoolExecutor类中提供了四个构造方法:
public class ThreadPoolExecutor extends AbstractExecutorService {
.....
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, handler);
}
...
}
从上面的代码可以得知,ThreadPoolExecutor继承了AbstractExecutorService(实现ExecutorService接口)类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。
下面解释下一下构造器中各个参数的含义:
corePoolSize:核心池的大小,在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把新加入的任务放到缓存队列当中;
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(true)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue:一个阻塞队列,用来存储等待执行的任务,一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。
threadFactory:线程工厂,主要用来创建线程;
handler:表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
在java doc中,并不提倡我们直接使用new ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池。
3 线程池的创建
线程池通过java.util.concurrent.Executors工厂类来创建。该工厂类包含以下几个静态工厂方法来创建:
1. newCachedThreadPool():创建一个具有缓存功能的线程池,当来了任务就创建线程运行,这些线程将会被缓存在线程池中。返回一个ExecutorService接口对象,代表一个线程池(内部代码实际创建的对象是ThreadPoolExecutor)。当线程空闲超过60秒,就销毁线程。
2. newFixedThreadPool(int nThreads):创建一个可以重用的、具有固定线程数的线程池。返回一个ExecutorService对象,代表一个线程池(内部代码实际创建的对象是ThreadPoolExecutor)。
3. newSingleThreadExecutor():创建一个只有单线程的线程池,它相当于newFixedThreadPool(1)。返回一个ExecutorService对象,代表一个线程池(内部代码实际创建的对象是ThreadPoolExecutor)。
4. newScheduledThreadPool(int corePoolSize):创建具有指定线程数的线程池,返回ScheduledExecutorService对象。它可以在指定延迟后执行线程任务(内部代码实际创建的对象是ScheduledThreadPoolExecutor)。
5. newSingleThreadScheduledExecutor():创建只有一条线程的线程池,它相当于newScheduledThreadPool(1)。它可以在指定延迟后执行线程任务。(内部代码实际创建的对象是DelegatedScheduledExecutorService,该类实际上是对ScheduledThreadPoolExecutor进行了包装)。
4 ExecutorService接口
代表一个线程池,只要线程池中有空闲线程立即执行线程任务,程序只要将一个Runnable对象或Callable对象提交给该线程池即可,该线程池就会尽快执行该任务。
1. execute() 这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
2. Future<?> submit(Runnable task):将一个Runnable对象提交给指定的线程池,线程池将在有空闲线程时执行Runnable对象代表的任务,其中Future对象代表Runnable任务的返回值,但run方法没有返回值,所有Future对象在run方法执行结束以后返回null,但可以调用Future的isDone等方法来获得Runnable对象的执行状态。
3. <T> Future<T> submit(Runnable task, T result):result显示指定线程执行结束后的返回值,所有Future对象将在run方法执行结束后返回result。 通过Future.get();该方法会阻塞线程等待该Runnable执行结束。
5 ThreadPoolExecutor 线程池的线程的关闭
shutdown():启动线程池的关闭序列,执行之后不再接受新任务,但会将之前所有已提交任务执行完毕,当线程池中的(所有任务)(包含阻塞队列的线程)都执行完毕后,线程池中的线程都会死亡。线程池也终止了...
shutdownNow():该方法会试图请求中断正在执行的线程(别忘了对线程的睡眠的终端异常加以处理),停止处理在等待的任务,并返回等待执行的任务列表。
newCachedThreadPool创建线程池示例代码:
public class Main {
static Scanner sc = new Scanner(System.in);
/**
* 并不提倡我们直接使用new ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池。
*
* @param args
*/
public static void main(String[] args) {
/**
* Executors.newCachedThreadPool()
*
* 核心池为0,
* 最大线程池为 Integer.MAX_NUM
*
* KeepAliveTime 默认为 60L 秒
*
*
* 通过excute执行一个Runnable时
* 1.判断该线程池是否有空闲线程,有,则由其中一个空闲线程执行该Runnable
* 没有,则创建一个新的线程,并执行该Runnable,加入线程池。
* 2.线程执行完成后,成为空闲线程,并缓存在线程池中,缓存时间为 KeepAliveTime。
*
*/
ThreadPoolExecutor tpe = (ThreadPoolExecutor) Executors.newCachedThreadPool();
// 改变空闲线程缓存的时间
tpe.setKeepAliveTime(5, TimeUnit.SECONDS);
int i = 1;
while (true) {
sc.next();
Work work = new Work("A" + i++);
tpe.execute(work);
}
}
static class Work implements Runnable {
private String name;
public Work(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "执行了<" + name + ">work开始");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "执行了<" + name + ">work结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
newFixedThreadPool创建线程池示例代码:
public class Main2 {
static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
/**
* Executors.newFixedThreadPool(3);
*
* 核心池为 3, 最大线程池为 3
*
* KeepAliveTime 默认为 0 秒
* 通过excute执行一个Runnable时
* 1.判断该线程池的核心池数量是否已满,否:则创建一个新的线程,并执行该Runnable,加入核心池中。
* 是,则判断核心池是否有空闲线程,有:则由其中一个空闲线程执行该Runnable 没有,加入链表阻塞队列,等待空闲线程。
*
* 2.线程执行完成后,成为空闲线程,并永久缓存在核心池中。
*/
ThreadPoolExecutor tpe = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
// ThreadPoolExecutor tpe = (ThreadPoolExecutor)
// Executors.newSingleThreadExecutor();
// tpe.prestartAllCoreThreads();//将核心池的线程数量缓存满(提前创建CoreSize个线程加入核心池)
// tpe.prestartCoreThread();//提前创建1个线程加入核心池
tpe.setKeepAliveTime(2, TimeUnit.SECONDS);
//允许核心池的线程超时。KeepAliveTime必须大于0
tpe.allowCoreThreadTimeOut(true);
int i = 1;
while (true) {
int poolSize = tpe.getPoolSize();
System.out.println("线程池实际缓存的线程数量:"+poolSize);
sc.next();
Work work = new Work("A" + i++);
tpe.execute(work);
}
}
static class Work implements Runnable {
private String name;
public Work(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "执行了<" + name + ">work开始");
Thread.sleep(4000);
System.out.println(Thread.currentThread().getName() + "执行了<" + name + ">work结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
ScheduledThreadPoolExecutor创建线程池示例代码:
public class Main3 {
static Scanner sc = new Scanner(System.in);
public static void main(String[] args) {
/**
* Executors.newScheduledThreadPool(3)
* 核心池为 3,
* 最大线程池为 Integer.MAX_VALUE
*
* KeepAliveTime 默认为 0 秒
*
* 通过excute执行一个Runnable时
* 1.判断核心池是否有空闲线程,有:则由其中一个空闲线程执行该Runnable
* 没有,则创建一个新的线程,并执行该Runnable,加入核心池中。
*
* 通过 schedule(Runnable command,long delay,TimeUnit unit) 执行一个Runnable时
*
* 1.判断核心池是否有空闲线程,有:则由其中一个空闲线程延时delay/unit时间后执行该Runnable
* 没有,则创建一个新的线程,并延时delay/unit时间执行该Runnable,加入核心池中。
*
* 2.线程执行完成后,成为空闲线程,并永久缓存在核心池中。
*/
ScheduledThreadPoolExecutor stpe = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(3);
// ScheduledThreadPoolExecutor stpe = (ScheduledThreadPoolExecutor) Executors.newSingleThreadScheduledExecutor();
int i = 1;
while (true) {
sc.next();
Work work = new Work("a" + i++);
// stpe.execute(work);
stpe.schedule(work, 2, TimeUnit.SECONDS);
}
}
static class Work implements Runnable {
private String name;
public Work(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "执行了<" + name + ">work开始");
Thread.sleep(4000);
System.out.println(Thread.currentThread().getName() + "执行了<" + name + ">work结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程池的执行(提交任务)示例代码:
public class Main {
/**
* 线程池的执行(提交任务)
*
* @param args
*/
public static void main(String[] args) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
MyRunnable mr = new MyRunnable();
// Future<?> submit = executor.submit(mr);
// Future<String> submit = executor.submit(mr, "aaaa");//该任务执行完成后将返回
// "aaa"
MyCallable mc = new MyCallable();
Future<String> submit = executor.submit(mc);
try {
String object = submit.get();// 等待,阻塞 执行该mr任务的子线程 执行完,返回结果
System.err.println("main:主线程执行了...XXXX object:" + object);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "执行了run..");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "执行了run..完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
try {
System.out.println(Thread.currentThread().getName() + "执行了run..");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "执行了run..完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "AAAAA.....";
}
}
}
线程池的关闭示例代码:
public class Main {
/**
* 线程池的关闭
*
* @param args
*/
public static void main(String[] args) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
executor.execute(new MyTask("a"));
executor.execute(new MyTask("b"));
executor.execute(new MyTask("c"));
executor.execute(new MyTask("d"));
executor.execute(new MyTask("e"));
// executor.shutdown();
// executor.execute(new MyTask("f"));//线程池停止程序启动后,不在接受新的任务
try {
Thread.sleep(2000);
List<Runnable> shutdownNow = executor.shutdownNow();
System.out.println("shutdownNow:" + shutdownNow.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class MyTask implements Runnable {
private String name;
public MyTask(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println("开始进入try的代码...");
for (int i = 0; i < 10 && !Thread.currentThread().isInterrupted(); i++) {
System.out.println(Thread.currentThread().getName() + "->>" + name + ">>>" + i);
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}