1. 程式人生 > >Java四種執行緒池的使用以及callable future整理

Java四種執行緒池的使用以及callable future整理

Java通過Executors提供四種執行緒池,分別為:
newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。
newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。

newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

(1) newCachedThreadPool


建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。示例程式碼如下:

  1. package test;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class ThreadPoolExecutorTest {  
  5.  public static void main(String[] args) {  
  6.   ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
  7.   for (int i = 0; i < 10; i++) {  
  8.    final int index = i;  
  9.    try {  
  10.     Thread.sleep(index * 1000);  
  11.    } catch (InterruptedException e) {  
  12.     e.printStackTrace();  
  13.    }  
  14.    cachedThreadPool.execute(new Runnable() {  
  15.     public void run() {  
  16.      System.out.println(index);  
  17.     }  
  18.    });  
  19.   }  
  20.  }  
  21. }

執行緒池為無限大,當執行第二個任務時第一個任務已經完成,會複用執行第一個任務的執行緒,而不用每次新建執行緒。
 
(2) newFixedThreadPool
建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。示例程式碼如下:

Java程式碼  收藏程式碼
  1. package test;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class ThreadPoolExecutorTest {  
  5.  public static void main(String[] args) {  
  6.   ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
  7.   for (int i = 0; i < 10; i++) {  
  8.    final int index = i;  
  9.    fixedThreadPool.execute(new Runnable() {  
  10.     public void run() {  
  11.      try {  
  12.       System.out.println(index);  
  13.       Thread.sleep(2000);  
  14.      } catch (InterruptedException e) {  
  15.       e.printStackTrace();  
  16.      }  
  17.     }  
  18.    });  
  19.   }  
  20.  }  
  21. }  
 
因為執行緒池大小為3,每個任務輸出index後sleep 2秒,所以每兩秒列印3個數字。
定長執行緒池的大小最好根據系統資源進行設定。如Runtime.getRuntime().availableProcessors()

(3)  newScheduledThreadPool
建立一個定長執行緒池,支援定時及週期性任務執行。延遲執行示例程式碼如下:

Java程式碼  收藏程式碼
  1. package test;  
  2. import java.util.concurrent.Executors;  
  3. import java.util.concurrent.ScheduledExecutorService;  
  4. import java.util.concurrent.TimeUnit;  
  5. public class ThreadPoolExecutorTest {  
  6.  public static void main(String[] args) {  
  7.   ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
  8.   scheduledThreadPool.schedule(new Runnable() {  
  9.    public void run() {  
  10.     System.out.println("delay 3 seconds");  
  11.    }  
  12.   }, 3, TimeUnit.SECONDS);  
  13.  }  
  14. }  

 
表示延遲3秒執行。

定期執行示例程式碼如下:
  1. package test;  
  2. import java.util.concurrent.Executors;  
  3. import java.util.concurrent.ScheduledExecutorService;  
  4. import java.util.concurrent.TimeUnit;  
  5. public class ThreadPoolExecutorTest {  
  6.  public static void main(String[] args) {  
  7.   ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
  8.   scheduledThreadPool.scheduleAtFixedRate(new Runnable() {  
  9.    public void run() {  
  10.     System.out.println("delay 1 seconds, and excute every 3 seconds");  
  11.    }  
  12.   }, 13, TimeUnit.SECONDS);  
  13.  }  
  14. }  

 
表示延遲1秒後每3秒執行一次。

(4) newSingleThreadExecutor
建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。示例程式碼如下:

Java程式碼  收藏程式碼
  1. package test;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class ThreadPoolExecutorTest {  
  5.  public static void main(String[] args) {  
  6.   ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();  
  7.   for (int i = 0; i < 10; i++) {  
  8.    final int index = i;  
  9.    singleThreadExecutor.execute(new Runnable() {  
  10.     public void run() {  
  11.      try {  
  12.       System.out.println(index);  
  13.       Thread.sleep(2000);  
  14.      } catch (InterruptedException e) {  
  15.       e.printStackTrace();  
  16.      }  
  17.     }  
  18.    });  
  19.   }  
  20.  }  
  21. }  

 
結果依次輸出,相當於順序執行各個任務。

你可以使用JDK自帶的監控工具來監控我們建立的執行緒數量,執行一個不終止的執行緒,建立指定量的執行緒,來觀察:
工具目錄:C:\Program Files\Java\jdk1.6.0_06\bin\jconsole.exe
執行程式做稍微修改:

Java程式碼  收藏程式碼
  1. package test;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. public class ThreadPoolExecutorTest {  
  5.  public static void main(String[] args) {  
  6.   ExecutorService singleThreadExecutor = Executors.newCachedThreadPool();  
  7.   for (int i = 0; i < 100; i++) {  
  8.    final int index = i;  
  9.    singleThreadExecutor.execute(new Runnable() {  
  10.     public void run() {  
  11.      try {  
  12.       while(true) {  
  13.        System.out.println(index);  
  14.        Thread.sleep(10 * 1000);  
  15.       }  
  16.      } catch (InterruptedException e) {  
  17.       e.printStackTrace();  
  18.      }  
  19.     }  
  20.    });  
  21.    try {  
  22.     Thread.sleep(500);  
  23.    } catch (InterruptedException e) {  
  24.     e.printStackTrace();  
  25.    }  
  26.   }  
  27.  }  
  28. }  

 
效果如下:

 

選擇我們執行的程式:

監控執行狀態

轉載自:http://cuisuqiang.iteye.com/blog/2019372    

http://blog.csdn.net/hupitao/article/details/24453083

 Callable介面類似於Runnable,從名字就可以看出來了,但是Runnable不會返回結果,並且無法丟擲返回結果的異常,而Callable功能更強大一些,被執行緒執行後,可以返回值,這個返回值可以被Future拿到,也就是說,Future可以拿到非同步執行任務的返回值,下面來看一個簡單的例子:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CallableAndFuture {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {
        Callable<Integer> callable = <span class="hljs-keyword">new</span> Callable<Integer>() {
            <span class="hljs-keyword">public</span> Integer <span class="hljs-title">call</span>() throws Exception {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Random().nextInt(<span class="hljs-number">100</span>);
            }
        };
        FutureTask<Integer> future = <span class="hljs-keyword">new</span> FutureTask<Integer>(callable);
        <span class="hljs-keyword">new</span> Thread(future).start();
        <span class="hljs-keyword">try</span> {
            Thread.sleep(<span class="hljs-number">5000</span>);<span class="hljs-comment">// 可能做一些事情</span>
            System.<span class="hljs-keyword">out</span>.println(future.<span class="hljs-keyword">get</span>());
        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
            e.printStackTrace();
        } <span class="hljs-keyword">catch</span> (ExecutionException e) {
            e.printStackTrace();
        }
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul>

       FutureTask實現了兩個介面,Runnable和Future,所以它既可以作為Runnable被執行緒執行,又可以作為Future得到Callable的返回值,那麼這個組合的使用有什麼好處呢?假設有一個很耗時的返回值需要計算,並且這個返回值不是立刻需要的話,那麼就可以使用這個組合,用另一個執行緒去計算返回值,而當前執行緒在使用這個返回值之前可以做其它的操作,等到需要這個返回值時,再通過Future得到,豈不美哉!這裡有一個Future模式的介紹:http://openhome.cc/Gossip/DesignPattern/FuturePattern.htm
       下面來看另一種方式使用Callable和Future,通過ExecutorService的submit方法執行Callable,並返回Future,程式碼如下:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CallableAndFuture {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        Future<Integer> future = threadPool.submit(<span class="hljs-keyword">new</span> Callable<Integer>() {
            <span class="hljs-keyword">public</span> Integer <span class="hljs-title">call</span>() throws Exception {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Random().nextInt(<span class="hljs-number">100</span>);
            }
        });
        <span class="hljs-keyword">try</span> {
            Thread.sleep(<span class="hljs-number">5000</span>);<span class="hljs-comment">// 可能做一些事情</span>
            System.<span class="hljs-keyword">out</span>.println(future.<span class="hljs-keyword">get</span>());
        } <span class="hljs-keyword">catch</span> (InterruptedException e) {
            e.printStackTrace();
        } <span class="hljs-keyword">catch</span> (ExecutionException e) {
            e.printStackTrace();
        }
    }
}</code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li></ul>

       程式碼是不是簡化了很多,ExecutorService繼承自Executor,它的目的是為我們管理Thread物件,從而簡化併發程式設計,Executor使我們無需顯示的去管理執行緒的生命週期,是JDK 5之後啟動任務的首選方式。
       執行多個帶返回值的任務,並取得多個返回值,程式碼如下:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> CallableAndFuture {
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        CompletionService<Integer> cs = <span class="hljs-keyword">new</span> ExecutorCompletionService<Integer>(threadPool);
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i < <span class="hljs-number">5</span>; i++) {
            final <span class="hljs-keyword">int</span> taskID = i;
            cs.submit(<span class="hljs-keyword">new</span> Callable<Integer>() {
                <span class="hljs-keyword">public</span> Integer <span class="hljs-title">call</span>() throws Exception {
                    <span class="hljs-keyword">return</span> taskID;
                }
            });
        }
        <span class="hljs-comment">// 可能做一些事情</span>
        <span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i = <span class="hljs-number">1</span>; i < <span class="hljs-number">5</span>; i++) {
            <span class="hljs-keyword">try</span> {
                System.<span class="hljs-keyword">out</span>.println(cs.take().<span class="hljs-keyword">get</span>());
            } <span class="hljs-keyword">catch</span> (InterruptedException e) {
                e.printStackTrace();
            } <span class="hljs-keyword">catch</span> (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
} </code><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li></ul><ul style="" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li></ul>

       其實也可以不使用CompletionService,可以先建立一個裝Future型別的集合,用Executor提交的任務返回值新增到集合中,最後遍歷集合取出資料,程式碼略。更新於2016-02-05,評論中就這個說法引發了討論,其實是我沒有講清楚,抱歉。這裡再闡述一下:提交到CompletionService中的Future是按照完成的順序排列的,這種做法中Future是按照新增的順序排列的。所以這兩種方式的區別就像評論中fishjam所描述的那樣。

http://www.cnblogs.com/dolphin0520/p/3949310.html

http://www.cnblogs.com/starcrm/p/5010863.html