2009-12-30 19 views
87

Tôi không phải là một lập trình viên java tốt, nó chỉ là sở thích của tôi, nhưng tôi háo hức muốn biết nhiều hơn những thứ trung bình.Java: Làm thế nào để mở rộng các luồng theo lõi CPU?

Tôi muốn giải quyết vấn đề toán học với nhiều chuỗi trong java. vấn đề toán học của tôi có thể được chia thành các đơn vị công việc, mà tôi muốn giải quyết trong một số chủ đề.

nhưng tôi không muốn có một số lượng cố định của các chủ đề làm việc trên nó, nhưng thay vì một số tiền coresponding của các chủ đề với số lượng lõi CPU. và vấn đề của tôi là, tôi không thể tìm thấy một hướng dẫn dễ dàng trên internet cho việc này. tất cả những gì tôi tìm thấy là những ví dụ với các chủ đề cố định.

Vì vậy, bạn có thể giúp tôi với một liên kết đến một gia sư tốt hay có thể cho tôi một ví dụ dễ dàng và tốt? Điều đó sẽ thực sự tốt đẹp :)

Trả lời

99

Bạn có thể xác định số lượng quy trình có sẵn cho Máy ảo Java bằng cách sử dụng phương pháp Thời gian chạy tĩnh, availableProcessors. Khi bạn đã xác định số lượng bộ vi xử lý sẵn có, hãy tạo số lượng chuỗi đó và chia nhỏ công việc của bạn cho phù hợp.

Cập nhật: Để làm rõ hơn, một Chủ đề chỉ là một đối tượng trong Java, vì vậy bạn có thể tạo nó giống như bạn sẽ tạo ra bất kỳ đối tượng nào khác. Vì vậy, hãy nói rằng bạn gọi phương thức trên và thấy rằng nó trả về 2 bộ vi xử lý. Tuyệt vời. Bây giờ, bạn có thể tạo ra một vòng lặp tạo ra một Thread mới, và tách công việc ra khỏi luồng đó và kích hoạt chuỗi đó. Dưới đây là một số psuedocode để chứng minh những gì tôi có nghĩa là:

int processors = Runtime.getRuntime().availableProcessors(); 
for(int i=0; i < processors; i++) { 
    Thread yourThread = new AThreadYouCreated(); 
    // You may need to pass in parameters depending on what work you are doing and how you setup your thread. 
    yourThread.start(); 
} 

Để biết thêm thông tin về tạo chủ đề của riêng bạn, head to this tutorial. Ngoài ra, bạn có thể muốn xem Thread Pooling để tạo chuỗi.

+12

Điều này về cơ bản là chính xác, nhưng hãy cẩn thận về hiệu năng trên bộ vi xử lý được tiếp thị với "siêu phân luồng" của Intel. Trên quad-core, điều này sẽ trả về 8 thay vì 4, nhưng hiệu suất của bạn thực sự có thể bắt đầu giảm sau 4 chủ đề - vì vậy điểm chuẩn của riêng tôi cho tôi biết :) – xcut

+0

Xin chào, okay, không biết, điều này là có thể. nhưng khi tôi chia một nhiệm vụ thành một số đơn vị công việc và tôi cần tất cả các giải pháp một phần cho bước cuối cùng, thì việc này được thực hiện như thế nào? Khi tôi có một số "yourThreads" làm thế nào để tôi sử dụng join() cho điều này, bởi vì tôi không nhìn thấy, làm thế nào một số chủ đề được phân biệt? :) BTW: liên kết của bạn đến Thread Pooling dẫn tôi đến http://www.ibm.com/developerworks/library/j-jtp0730.html :) –

+4

Xem ví dụ tại đây: http: // java. sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ExecutorService.html Nó sẽ cho bạn biết một cách sắp xếp hợp lý hơn để tạo và quản lý nhóm luồng ... Nó có vẻ phức tạp hơn lúc đầu, nhưng như với hầu hết mọi thứ, nó phức tạp hơn bởi vì nếu nó đơn giản hơn, bạn chỉ cần đạt giới hạn sớm hơn. –

4

Trên lớp Runtime, có một phương thức được gọi là availableProcessors(). Bạn có thể sử dụng nó để tìm ra số lượng CPU bạn có. Kể từ khi chương trình của bạn là CPU bị ràng buộc, bạn có thể sẽ muốn có (nhiều nhất) một sợi cho mỗi CPU có sẵn.

+0

Xin chào Jason và Eric (Tôi sử dụng một bình luận cho cả hai câu trả lời của bạn, bởi vì nó cơ bản giống nhau). không sao, thật thú vị khi kiểm tra, nhưng đây sẽ là phần đầu tiên. Khi tôi có số lõi, tôi phải có các chủ đề biến đổi như số lượng lõi này. Tôi đã thử ví dụ này trước khi http://openbook.galileodesign.de/javainsel5/javainsel09_003.htm#Rxx747java09003040002E31F0491F9 (tiếng Đức!) Và nó sử dụng một chuỗi cố định. Nhưng tôi muốn có cùng một lập trình sử dụng 2 lõi trong môi trường lõi kép và 4 lõi trong môi trường lõi tứ. Tôi không muốn thay đổi nó bằng tay. Điều này có khả thi không? THX! :) –

+0

@Andreas - Xem các cập nhật tôi đã thực hiện cho bài đăng của mình.Tôi nghĩ điều đó sẽ giúp làm rõ vấn đề. – JasCav

59

Bạn có thể muốn xem xét khung công tác java.util.concurrent cho công cụ này. Cái gì như:

ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 
// Do work using something like either 
e.execute(new Runnable() { 
     public void run() { 
      // do one task 
     } 
    }); 

hoặc

Future<String> future = pool.submit(new Callable<String>() { 
     public String call() throws Exception { 
      return null; 
     } 
    }); 
    future.get(); // Will block till result available 

Đây là đẹp hơn rất nhiều so với việc đối phó với hồ bơi thread riêng của bạn, vv

+0

Xin chào DaveC, hmmm, chưa biết điều đó trước đây, vì vậy tôi sẽ xem xét điều này. Và nó có thể được thu nhỏ theo lõi cpu có sẵn không? Bởi vì tôi không thể thấy điều đó trong các ví dụ ngắn. Trân trọng, Andreas –

+3

java.util.concurrent có khả năng mở rộng cao –

+4

Một nhóm kích thước cố định với số lượng bộ xử lý có sẵn thường tối ưu cho các quy trình liên kết CPU. Ví dụ đầu tiên ở đây là tất cả những gì bạn cần làm. –

4

Cách thông thường là Runtime.getRuntime() availableProcessors() phương pháp. Trên hầu hết các CPU tiêu chuẩn, bạn sẽ trả về số lượng chỉ số tối ưu (không phải là số lõi CPU thực tế) tại đây. Vì vậy, đây là những gì bạn đang tìm kiếm.

Ví dụ:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 

ĐỪNG quên đóng cửa dịch vụ thi hành di chúc như thế này (hoặc chương trình của bạn sẽ không thoát):

service.shutdown(); 

Dưới đây chỉ là một phác thảo nhanh chóng làm thế nào để thiết lập mã MT dựa trên tương lai (ngoại tuyến, để minh hoạ):

CompletionService<YourCallableImplementor> completionService = 
    new ExecutorCompletionService<YourCallableImplementor>(service); 
    ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>(); 
    for (String computeMe : elementsToCompute) { 
     futures.add(completionService.submit(new YourCallableImplementor(computeMe))); 
    } 

Sau đó, bạn cần theo dõi số lượng kết quả bạn mong đợi và lấy chúng như thế này:

try { 
    int received = 0; 
    while (received < elementsToCompute.size()) { 
    Future<YourCallableImplementor> resultFuture = completionService.take(); 
    YourCallableImplementor result = resultFuture.get(); 
    received++; 
    } 
} finally { 
    service.shutdown(); 
} 
+2

cuộc gọi tắt máy nên được đặt trong cố gắng cuối cùng –

+0

@ChristopheRoussy bạn rất đúng, tôi đã sửa đổi đoạn mã cho phù hợp, cảm ơn bạn! – fl0w

6

Lựa chọn 1:

newWorkStealingPool từ Executors

public static ExecutorService newWorkStealingPool() 

Tạo một tác phẩm đánh cắp bơi thread sử dụng tất cả các bộ xử lý có sẵn như là mức song song mục tiêu của nó.

Với API này, bạn không cần phải chuyển số lõi tới ExecutorService.

Thực hiện các API này từ grepcode

/** 
    * Creates a work-stealing thread pool using all 
    * {@link Runtime#availableProcessors available processors} 
    * as its target parallelism level. 
    * @return the newly created thread pool 
    * @see #newWorkStealingPool(int) 
    * @since 1.8 
    */ 
    public static ExecutorService newWorkStealingPool() { 
     return new ForkJoinPool 
      (Runtime.getRuntime().availableProcessors(), 
      ForkJoinPool.defaultForkJoinWorkerThreadFactory, 
      null, true); 
    } 

Phương án 2:

newFixedThreadPool API từ Executors hoặc other newXXX constructors, mà trả về ExecutorService

public static ExecutorService newFixedThreadPool(int nThreads) 

thay nThreads vớiRuntime.getRuntime().availableProcessors()

Lựa chọn 3:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize, 
         int maximumPoolSize, 
         long keepAliveTime, 
         TimeUnit unit, 
         BlockingQueue<Runnable> workQueue) 

qua Runtime.getRuntime().availableProcessors() như tham số để maximumPoolSize.