1. 程式人生 > 其它 >java內建執行緒池

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()方法,關閉執行緒池