java内置线程池

Java线程池 - (二)内置线程池
ExecutorService接口是Java内置的线程池接口,整体的继承关系如下:

 

 

 

 

其常用方法有:

void shutdown() - 启动一次顺序关闭,执行以前提交的任务,但不接受新任务
List<Runnable> shutdownNow() - 停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表
<T> Future<T> submit(Callable<T> task) - 执行带返回值的任务,返回一个Future对象
Future<?> submit(Runnable task) - 执行 Runnable 任务,并返回一个表示该任务的 Future
<T> Future<T> submit(Runnable task, T result) - 执行 Runnable 任务,并返回一个表示该任务的 Future

 


获取ExecutorService
通过Executors类中的静态方法来获取ExecutorService

1.static ExecutorService newCachedThreadPool()
创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建

2.static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行;

3.static ExecutorService newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池

4.static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建。

5.static ExecutorService newSingleThreadExecutor()
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

6.static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建

 

newCachedThreadPool
static ExecutorService newCachedThreadPool()方法定义如下:

 

/**
* Creates a thread pool that creates new threads as needed, but
* will reuse previously constructed threads when they are
* available. These pools will typically improve the performance
* of programs that execute many short-lived asynchronous tasks.
* Calls to {@code execute} will reuse previously constructed
* threads if available. If no existing thread is available, a new
* thread will be created and added to the pool. Threads that have
* not been used for sixty seconds are terminated and removed from
* the cache. Thus, a pool that remains idle for long enough will
* not consume any resources. Note that pools with similar
* properties but different details (for example, timeout parameters)
* may be created using {@link ThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

它是一个可以无限扩大的线程池;
它比较适合处理执行时间比较小的任务;
corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;
keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。
如下的例子:

public class MyTest01 {

public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MyRunnable(i));
}
}


}

/**
* 任务类
*/
class MyRunnable implements Runnable {
private int id;

public MyRunnable(int id) {
this.id = id;
}

@Override
public void run() {
//获取线程的名称,并打印
String name = Thread.currentThread().getName();
System.out.println(name + "执行了任务..." + id);
}
}

输出结果为:

pool-1-thread-8执行了任务...8
pool-1-thread-9执行了任务...9
pool-1-thread-1执行了任务...1
pool-1-thread-7执行了任务...7
pool-1-thread-3执行了任务...3
pool-1-thread-6执行了任务...6
pool-1-thread-5执行了任务...5
pool-1-thread-4执行了任务...4
pool-1-thread-2执行了任务...2
pool-1-thread-10执行了任务...10

 

newFixedThreadPool
static ExecutorService newFixedThreadPool(int nThreads) 方法定义如下:

/**
* Creates a thread pool that reuses a fixed number of threads
* operating off a shared unbounded queue. At any point, at most
* {@code nThreads} threads will be active processing tasks.
* If additional tasks are submitted when all threads are active,
* they will wait in the queue until a thread is available.
* If any thread terminates due to a failure during execution
* prior to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@link ExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads in the pool
* @return the newly created thread pool
* @throws IllegalArgumentException if {@code nThreads <= 0}
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

 

它是一种固定大小的线程池;
corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads;
keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效;
阻塞队列采用了LinkedBlockingQueue,它是一个无界队列;由于阻塞队列是一个无界队列,因此永远不可能拒绝任务;
由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。
如下的例子,线程个数为3:

ExecutorService executorService = Executors.newFixedThreadPool(3, new ThreadFactory() {
int n = 1;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "自定义的线程名称" + (n++));
}
});
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MyRunnable(i));
}

 

输出结果如下:

自定义的线程名称3执行了任务...3
自定义的线程名称2执行了任务...2
自定义的线程名称1执行了任务...1
自定义的线程名称3执行了任务...4
自定义的线程名称3执行了任务...5
自定义的线程名称2执行了任务...6
自定义的线程名称3执行了任务...7
自定义的线程名称3执行了任务...9
自定义的线程名称2执行了任务...8
自定义的线程名称1执行了任务...10

 

newSingleThreadExecutor
static ExecutorService newSingleThreadExecutor()方法定义如下:

/**
* Creates an Executor that uses a single worker thread operating
* off an unbounded queue. (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newFixedThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

 

它只会创建一条工作线程处理任务;
采用的阻塞队列为LinkedBlockingQueue;
如下的例子:

ExecutorService executorService = Executors.newSingleThreadExecutor();
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MyRunnable(i));
}

 

输出结果如下:

pool-1-thread-1执行了任务...1
pool-1-thread-1执行了任务...2
pool-1-thread-1执行了任务...3
pool-1-thread-1执行了任务...4
pool-1-thread-1执行了任务...5
pool-1-thread-1执行了任务...6
pool-1-thread-1执行了任务...7
pool-1-thread-1执行了任务...8
pool-1-thread-1执行了任务...9
pool-1-thread-1执行了任务...10

可见只有一个线程

shutdown() vs shutdownNow()
shutdown()方法,仅仅是不再接受新的任务,以前的任务还会继续执行
如下例子:

ExecutorService executorService = Executors.newSingleThreadExecutor();
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MRunnable(i));
}
//关闭线程
executorService.shutdown();
executorService.submit(new MyRunnable(888));

 

此时输出结果为:

pool-1-thread-1执行了任务...1
pool-1-thread-1执行了任务...2
pool-1-thread-1执行了任务...3
pool-1-thread-1执行了任务...4
pool-1-thread-1执行了任务...5
pool-1-thread-1执行了任务...6
pool-1-thread-1执行了任务...7
pool-1-thread-1执行了任务...8
pool-1-thread-1执行了任务...9
pool-1-thread-1执行了任务...10

 


可见,关闭后new MyRunnable(888)并没有执行,但其它已加入的任务还是会继续执行

shutdownNow()立刻关闭线程池,如果线程池中还有缓存的任务没有执行,则取消执行,并返回这些没有执行的任务
如下的例子:

ExecutorService executorService = Executors.newSingleThreadExecutor();
//提交任务
for (int i = 1; i <= 10; i++) {
executorService.submit(new MRunnable(i));
}
//关闭线程
List<Runnable> list = executorService.shutdownNow();
System.out.println("list = " + list);

此时的输出结果如下:

 

 

 


ScheduledExecutorService
ScheduledExecutorService是ExecutorService的子接口,具备了延迟运行或定期执行任务的能力

常用获取方式如下(在Executors这个工具类中):


1.static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个可重用固定线程数的线程池且允许延迟运行或定期执行任务; 2.static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) 创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建,且允许延迟运行或定期执行任务; 3.static ScheduledExecutorService newSingleThreadScheduledExecutor() 创建一个单线程执行程序,它允许在给定延迟后运行命令或者定期地执行。 4.static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。

 

ScheduledExecutorService常用方法如下:

1.<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
延迟时间单位是unit,数量是delay的时间后执行callable。

2.ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
延迟时间单位是unit,数量是delay的时间后执行command。

3.ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
延迟时间单位是unit,数量是initialDelay的时间后,每间隔period时间重复执行一次command。

4.ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。

 

scheduleAtFixedRate和scheduleWithFixedDelay的区别

1.scheduleAtFixedRate

可以看到如果任务执行时间超出周期时,下一次任务会立刻运行;就好像周期是一个有弹性的袋子,能装下运行时间的时候,是固定大小,装不下的时候就会被撑大,图像化表示如下:

 

 

 


2.scheduleWithFixedDelay

可以看到无论执行时间是多少,其结果都是在执行完毕后,停顿固定的时间,然后执行下一次任务,其图形化表示为:

 

 

 


延迟执行
如下简单延迟执行的例子:

public class ScheduleExecutorServiceDemo01 {
public static void main(String[] args) {

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
for (int i = 1; i <= 10; i++) {
scheduledExecutorService.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS);
}
System.out.println("main over");

}
}

/**
* 任务类
*/
class MyRunnable implements Runnable {
private int id;

public MyRunnable(int id) {
this.id = id;
}

@Override
public void run() {
//获取线程的名称,并打印
String name = Thread.currentThread().getName();
System.out.println(name + "执行了任务..." + id);
}
}

 

运行后,控制台的输出结果为:

main over
pool-1-thread-1执行了任务...1
pool-1-thread-2执行了任务...2
pool-1-thread-3执行了任务...3
pool-1-thread-1执行了任务...4
pool-1-thread-3执行了任务...5
pool-1-thread-3执行了任务...8
pool-1-thread-1执行了任务...7
pool-1-thread-2执行了任务...6
pool-1-thread-1执行了任务...10
pool-1-thread-3执行了任务...9

 

scheduleAtFixedRate
scheduleAtFixedRate例子,如下:

public class ScheduleExecutorServiceDemo02 {
public static void main(String[] args) {

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3, new ThreadFactory() {
int n = 1;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "自定义线程名-" + n++);
}
});
scheduledExecutorService.scheduleAtFixedRate(new MyRunnable02(1), 2, 2, TimeUnit.SECONDS);
}
}

/**
* 任务类
*/
class MyRunnable02 implements Runnable {
private int id;

public MyRunnable02(int id) {
this.id = id;
}

@Override
public void run() {
//获取线程的名称,并打印
try {
String name = Thread.currentThread().getName();
Thread.sleep(1500);

SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
String dateString = sdf.format(new Date());

System.out.println(dateString + "-" + name + "执行了任务..." + id);
} catch (Exception e) {
e.printStackTrace();

}
}

 

输出结果如下:

2021:11:27 16:05:06-自定义线程名-1执行了任务...1
2021:11:27 16:05:08-自定义线程名-1执行了任务...1
2021:11:27 16:05:10-自定义线程名-2执行了任务...1
2021:11:27 16:05:12-自定义线程名-2执行了任务...1
2021:11:27 16:05:14-自定义线程名-2执行了任务...1
2021:11:27 16:05:16-自定义线程名-2执行了任务...1

 

scheduleWithFixedDelay
scheduleWithFixedDelay例子,如下:

public class ScheduleExecutorServiceDemo03 {
public static void main(String[] args) {

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
int n = 1;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "自定义线程名-" + n++);
}
});
scheduledExecutorService.scheduleWithFixedDelay(new MyRunnable03(1), 1, 2, TimeUnit.SECONDS);
}
}

/**
* 任务类
*/
class MyRunnable03 implements Runnable {
private int id;

public MyRunnable03(int id) {
this.id = id;
}

@Override
public void run() {
//获取线程的名称,并打印
try {
String name = Thread.currentThread().getName();
Thread.sleep(2000);//每隔4秒才能看到输出

SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
String dateString = sdf.format(new Date());

System.out.println(dateString + "-" + name + "执行了任务..." + id);
} catch (Exception e) {
e.printStackTrace();
}
}
}

 

输出结果如下:

2021:11:27 16:15:59-自定义线程名-1执行了任务...1
2021:11:27 16:16:03-自定义线程名-1执行了任务...1
2021:11:27 16:16:07-自定义线程名-1执行了任务...1
2021:11:27 16:16:11-自定义线程名-1执行了任务...1
2021:11:27 16:16:15-自定义线程名-1执行了任务...1
2021:11:27 16:16:19-自定义线程名-1执行了任务...1

 

Future
Runnable接口有个问题,它的方法没有返回值。如果任务需要一个返回结果,那么只能保存到变量,还要提供额外的方法读取,非常不便。所以,Java标准库还提供了一个Callable接口,和Runnable接口比,它多了一个返回值

ExecutorService中如下的方法,返回一个Future,一个Future类型的实例代表一个未来能获取结果的对象

<T> Future<T> submit(Callable<T> task) 执行带返回值的任务,返回一个Future对象。
Future<?> submit(Runnable task) 执行 Runnable 任务,并返回一个表示该任务的 Future。
<T> Future<T> submit(Runnable task, T result) 执行 Runnable 任务,并返回一个表示该任务的 Future。

 


Future 的常用方法如下:

1.boolean cancel(boolean mayInterruptIfRunning)
试图取消对此任务的执行。

2.V get()
如有必要,等待计算完成,然后获取其结果。

3.V get(long timeout, TimeUnit unit)
如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。

4.boolean isCancelled()
如果在任务正常完成前将其取消,则返回 true5.boolean isDone()
如果任务已完成,则返回 true

 

如下的例子:

public class FutureDemo {
public static void main(String[] args) throws Exception {
//获取线程池对象
ExecutorService es = Executors.newCachedThreadPool();
//创建Callable类型的任务对象
Future<Integer> f = es.submit(new MyCall(1, 2));
//判断任务是否执行完成 取消
boolean done = f.isDone();
System.out.println("第一次判断任务是否完成:done = " + done);
boolean cancelled = f.isCancelled();
System.out.println("第一次判断任务是否取消:cancelled = " + cancelled);
//等待任务完成,一致等待,知道完成
Integer result = f.get();
System.out.println("任务执行结果:result = " + result);

done = f.isDone();
System.out.println("第二次判断任务是否完成:done = " + done);
cancelled = f.isCancelled();
System.out.println("第二次判断任务是否取消:cancelled = " + cancelled);

}
}

/**
* 计算2个整数的值
*/
class MyCall implements Callable<Integer> {
private int a;
private int b;

public MyCall(int a, int b) {
this.a = a;
this.b = b;
}

@Override
public Integer call() throws Exception {
String name = Thread.currentThread().getName();
System.out.println(getFormatTime() + " - " + name + " 准备开始计算...");
Thread.sleep(2000);
System.out.println(getFormatTime() + " - " + name + " 计算完成...");
return a + b;
}

private String getFormatTime() {
SimpleDateFormat sdf = new SimpleDateFormat("", Locale.SIMPLIFIED_CHINESE);
sdf.applyPattern("yyyy年MM月dd日 HH时mm分ss秒");
return sdf.format(System.currentTimeMillis());
}
}

 

输出结果如下:

第一次判断任务是否完成:done = false
第一次判断任务是否取消:cancelled = false
2021年11月27日 17时12分32秒 - pool-1-thread-1 准备开始计算...
2021年11月27日 17时12分34秒 - pool-1-thread-1 计算完成...
任务执行结果:result = 3
第二次判断任务是否完成:done = true
第二次判断任务是否取消:cancelled = false

 

例子
秒杀商品

实现如下:

/**
* 任务类
* 1.包含了商品数量 客户名称 秒杀手机的行为
*/
public class MyTask implements Runnable {
//设计一个变量,用于表示商品的数量
private static int id = 10;
//表示客户名称的变量
private String userName;

public MyTask(String userName) {
this.userName = userName;
}

@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(getFormatTime() + " " + userName + " 正在使用 " + threadName + " 参与任务...");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (MyTask.class) {
if (id > 0) {
System.out.println(" " + userName + "使用" + threadName + "秒杀:" + id-- + "号商品成功啦!");
} else {
System.out.println(" " + userName + "使用" + threadName + "秒杀失败啦!");
}
}
}

private String getFormatTime() {
SimpleDateFormat sdf = new SimpleDateFormat("", Locale.SIMPLIFIED_CHINESE);
sdf.applyPattern("yyyy年MM月dd日 HH时mm分ss秒");
return sdf.format(System.currentTimeMillis());
}
}


/**
* 主程序类,测试任务类
*/
public class MyTest {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(3,
5,
1,
TimeUnit.MINUTES,
new LinkedBlockingQueue<>(15));
//模拟20个客户
for (int i = 1; i <= 20; i++) {
MyTask myTask = new MyTask("客户" + i);
pool.submit(myTask);
}
//关闭线程池
pool.shutdown();
}
}

 

运行结果输出如下:

2021年11月27日 17时36分12秒 客户3 正在使用 pool-1-thread-3 参与任务...
2021年11月27日 17时36分12秒 客户19 正在使用 pool-1-thread-4 参与任务...
2021年11月27日 17时36分12秒 客户2 正在使用 pool-1-thread-2 参与任务...
2021年11月27日 17时36分12秒 客户1 正在使用 pool-1-thread-1 参与任务...
2021年11月27日 17时36分12秒 客户20 正在使用 pool-1-thread-5 参与任务...
客户3使用pool-1-thread-3秒杀:10号商品成功啦!
客户19使用pool-1-thread-4秒杀:9号商品成功啦!
客户2使用pool-1-thread-2秒杀:8号商品成功啦!
客户20使用pool-1-thread-5秒杀:7号商品成功啦!
客户1使用pool-1-thread-1秒杀:6号商品成功啦!
2021年11月27日 17时36分14秒 客户4 正在使用 pool-1-thread-3 参与任务...
2021年11月27日 17时36分14秒 客户5 正在使用 pool-1-thread-5 参与任务...
2021年11月27日 17时36分14秒 客户6 正在使用 pool-1-thread-1 参与任务...
2021年11月27日 17时36分14秒 客户7 正在使用 pool-1-thread-2 参与任务...
2021年11月27日 17时36分14秒 客户8 正在使用 pool-1-thread-4 参与任务...
客户4使用pool-1-thread-3秒杀:5号商品成功啦!
客户8使用pool-1-thread-4秒杀:4号商品成功啦!
客户6使用pool-1-thread-1秒杀:3号商品成功啦!
客户5使用pool-1-thread-5秒杀:2号商品成功啦!
2021年11月27日 17时36分16秒 客户9 正在使用 pool-1-thread-3 参与任务...
客户7使用pool-1-thread-2秒杀:1号商品成功啦!
2021年11月27日 17时36分16秒 客户10 正在使用 pool-1-thread-4 参与任务...
2021年11月27日 17时36分16秒 客户11 正在使用 pool-1-thread-1 参与任务...
2021年11月27日 17时36分16秒 客户12 正在使用 pool-1-thread-5 参与任务...
2021年11月27日 17时36分16秒 客户13 正在使用 pool-1-thread-2 参与任务...
客户9使用pool-1-thread-3秒杀失败啦!
客户11使用pool-1-thread-1秒杀失败啦!
客户13使用pool-1-thread-2秒杀失败啦!
客户12使用pool-1-thread-5秒杀失败啦!
客户10使用pool-1-thread-4秒杀失败啦!
2021年11月27日 17时36分18秒 客户15 正在使用 pool-1-thread-1 参与任务...
2021年11月27日 17时36分18秒 客户14 正在使用 pool-1-thread-3 参与任务...
2021年11月27日 17时36分18秒 客户16 正在使用 pool-1-thread-2 参与任务...
2021年11月27日 17时36分18秒 客户18 正在使用 pool-1-thread-4 参与任务...
2021年11月27日 17时36分18秒 客户17 正在使用 pool-1-thread-5 参与任务...
客户16使用pool-1-thread-2秒杀失败啦!
客户18使用pool-1-thread-4秒杀失败啦!
客户17使用pool-1-thread-5秒杀失败啦!
客户14使用pool-1-thread-3秒杀失败啦!
客户15使用pool-1-thread-1秒杀失败啦!

 

取款业务

代码如下:

/**
* 任务类
*/
public class MyTask implements Runnable{
//用户姓名
private String userName;
//用户的取款金额
private double money;
//总金额
private static double total = 1000;

public MyTask(String userName, double money) {
this.userName = userName;
this.money = money;
}

@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(getFormatTime() + " " + userName + " 正在准备使用 " + threadName + " 取款 " + money + "元");
synchronized (MyTask.class) {
if (total - money > 0) {
System.out.println(getFormatTime() + " " + userName + " 使用 " + threadName + " 取款 " + money + "元成功" + ",余额:" + (total - money));
total -= money;
} else {
System.out.println(getFormatTime() + " " + userName + " 使用 " + threadName + " 取款 " + money + "元失败" + ",余额:" + total);
}
}
}

private String getFormatTime() {
SimpleDateFormat sdf = new SimpleDateFormat("", Locale.SIMPLIFIED_CHINESE);
sdf.applyPattern("yyyy年MM月dd日 HH时mm分ss秒");
return sdf.format(System.currentTimeMillis());
}
}


public class MyTest {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2, new ThreadFactory() {
int id = 1;
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "ATM" + id++);
}
});
//模拟2个用户,取款
for (int i = 1; i <= 2; i++) {
MyTask myTask = new MyTask("客户" + i, 800);
pool.submit(myTask);
}
//关闭线程池
pool.shutdown();
}
}

 

输出结果如下:

2021年11月27日 21时40分03秒 客户2 正在准备使用 ATM2 取款 800.0元
2021年11月27日 21时40分03秒 客户1 正在准备使用 ATM1 取款 800.0元
2021年11月27日 21时40分03秒 客户2 使用 ATM2 取款 800.0元成功,余额:200.0
2021年11月27日 21时40分03秒 客户1 使用 ATM1 取款 800.0元失败,余额:200.0

 

总结
线程池的使用步骤可以归纳总结为五步 :

1.利用Executors工厂类的静态方法,创建线程池对象;
2.编写Runnable或Callable实现类的实例对象;
3.利用ExecutorService的submit方法或ScheduledExecutorService的schedule方法提交并执行线程任务
4.如果有执行结果,则处理异步执行结果(Future)
5.调用shutdown()方法,关闭线程池

 

本文摘自,如有侵权,请联系我!!!

posted @ 2022-03-11 10:24  高压锅里的大萝卜  阅读(86)  评论(0编辑  收藏  举报