神奇好望角 The Magical Cape of Good Hope

          庸人不必自擾,智者何需千慮?
          posts - 26, comments - 50, trackbacks - 0, articles - 11
            BlogJava :: 首頁(yè) ::  :: 聯(lián)系 :: 聚合  :: 管理

          非主流并發(fā)工具之 ForkJoinPool

          Posted on 2012-02-09 10:40 蜀山兆孨龘 閱讀(2692) 評(píng)論(2)  編輯  收藏 所屬分類(lèi): Java SE

          ForkJoinPool 是 Java SE 7 新功能“分叉/結(jié)合框架”的核心類(lèi),現(xiàn)在可能乏人問(wèn)津,但我覺(jué)得它遲早會(huì)成為主流。分叉/結(jié)合框架是一個(gè)比較特殊的線程池框架,專(zhuān)用于需要將一個(gè)任務(wù)不斷分解成子任務(wù)(分叉),再不斷進(jìn)行匯總得到最終結(jié)果(結(jié)合)的計(jì)算過(guò)程。比起傳統(tǒng)的線程池類(lèi) ThreadPoolExecutor,ForkJoinPool 實(shí)現(xiàn)了工作竊取算法,使得空閑線程能夠主動(dòng)分擔(dān)從別的線程分解出來(lái)的子任務(wù),從而讓所有的線程都盡可能處于飽滿(mǎn)的工作狀態(tài),提高執(zhí)行效率。

          ForkJoinPool 提供了三類(lèi)方法來(lái)調(diào)度子任務(wù):

          execute 系列
          異步執(zhí)行指定的任務(wù)。
          invokeinvokeAll
          執(zhí)行指定的任務(wù),等待完成,返回結(jié)果。
          submit 系列
          異步執(zhí)行指定的任務(wù)并立即返回一個(gè) Future 對(duì)象。

          子任務(wù)由 ForkJoinTask 的實(shí)例來(lái)代表。它是一個(gè)抽象類(lèi),JDK 為我們提供了兩個(gè)實(shí)現(xiàn):RecursiveTaskRecursiveAction,分別用于需要和不需要返回計(jì)算結(jié)果的子任務(wù)。ForkJoinTask 提供了三個(gè)靜態(tài)的 invokeAll 方法來(lái)調(diào)度子任務(wù),注意只能在 ForkJoinPool 執(zhí)行計(jì)算的過(guò)程中調(diào)用它們。

          ForkJoinPoolForkJoinTask 還提供了很多讓人眼花繚亂的公共方法,其實(shí)它們大多數(shù)都是其內(nèi)部實(shí)現(xiàn)去調(diào)用的,對(duì)于應(yīng)用開(kāi)發(fā)人員來(lái)說(shuō)意義不大。

          下面以統(tǒng)計(jì) D 盤(pán)文件個(gè)數(shù)為例。這實(shí)際上是對(duì)一個(gè)文件樹(shù)的遍歷,我們需要遞歸地統(tǒng)計(jì)每個(gè)目錄下的文件數(shù)量,最后匯總,非常適合用分叉/結(jié)合框架來(lái)處理:

          // 處理單個(gè)目錄的任務(wù)
          public class CountingTask extends RecursiveTask<Integer> {
              private Path dir;
          
              public CountingTask(Path dir) {
                  this.dir = dir;
              }
          
              @Override
              protected Integer compute() {
                  int count = 0;
                  List<CountingTask> subTasks = new ArrayList<>();
          
                  // 讀取目錄 dir 的子路徑。
                  try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
                      for (Path subPath : ds) {
                          if (Files.isDirectory(subPath, LinkOption.NOFOLLOW_LINKS)) {
                              // 對(duì)每個(gè)子目錄都新建一個(gè)子任務(wù)。
                              subTasks.add(new CountingTask(subPath));
                          } else {
                              // 遇到文件,則計(jì)數(shù)器增加 1。
                              count++;
                          }
                      }
          
                      if (!subTasks.isEmpty()) {
                          // 在當(dāng)前的 ForkJoinPool 上調(diào)度所有的子任務(wù)。
                          for (CountingTask subTask : invokeAll(subTasks)) {
                              count += subTask.join();
                          }
                      }
                  } catch (IOException ex) {
                      return 0;
                  }
                  return count;
              }
          }
          
          // 用一個(gè) ForkJoinPool 實(shí)例調(diào)度“總?cè)蝿?wù)”,然后敬請(qǐng)期待結(jié)果……
          Integer count = new ForkJoinPool().invoke(new CountingTask(Paths.get("D:/")));
              

          在我的筆記本上,經(jīng)多次運(yùn)行這段代碼,耗費(fèi)的時(shí)間穩(wěn)定在 600 豪秒左右。普通線程池(Executors.newCachedThreadPool())耗時(shí) 1100 毫秒左右,足見(jiàn)工作竊取的優(yōu)勢(shì)。

          結(jié)束本文前,我們來(lái)圍觀一個(gè)最神奇的結(jié)果:?jiǎn)尉€程算法(使用 Files.walkFileTree(...))比這兩個(gè)都快,平均耗時(shí) 550 毫秒!這警告我們并非引入多線程就能優(yōu)化性能,并須要先經(jīng)過(guò)多次測(cè)試才能下結(jié)論。


          評(píng)論

          # re: 非主流并發(fā)工具之 ForkJoinPool  回復(fù)  更多評(píng)論   

          2012-02-24 14:53 by 恩亠
          600毫秒?不是600秒嗎?那你的D盤(pán)也太干凈了,我的有上百萬(wàn)文件,根本就是半天沒(méi)有結(jié)果

          # re: 非主流并發(fā)工具之 ForkJoinPool  回復(fù)  更多評(píng)論   

          2012-02-24 22:48 by 蜀山兆孨龘
          確實(shí)是 600 毫秒,看來(lái)你下的片片太多了哈哈。上百萬(wàn)的文件數(shù)量即使在 Windows 里面右鍵看屬性都很慢……
          主站蜘蛛池模板: 梁河县| 许昌市| 惠来县| 财经| 山丹县| 兖州市| 长沙市| 巴林右旗| 喀什市| 馆陶县| 天镇县| 五大连池市| 合阳县| 浦东新区| 淮安市| 黄石市| 上饶市| 龙岩市| 齐齐哈尔市| 当涂县| 崇文区| 同德县| 汕头市| 舒城县| 新民市| 龙陵县| 饶河县| 伊金霍洛旗| 吐鲁番市| 望城县| 邯郸市| 当阳市| 米脂县| 巢湖市| 汉源县| 盐山县| 淳安县| 东源县| 广宁县| 横山县| 新竹县|