2012-10-17 39 views
6

Vì vậy, tôi có một chương trình mà tôi đã thực hiện cần phải gửi rất nhiều (như 10.000+) yêu cầu GET tới một URL và tôi cần nó càng nhanh càng tốt. Khi tôi lần đầu tiên tạo ra chương trình tôi chỉ cần đặt các kết nối vào một vòng lặp for nhưng nó đã thực sự chậm vì nó sẽ phải chờ đợi cho mỗi kết nối để hoàn thành trước khi tiếp tục. Tôi muốn làm cho nó nhanh hơn vì vậy tôi đã thử sử dụng các chủ đề và nó làm cho nó hơi nhanh hơn nhưng tôi vẫn không hài lòng.Tìm hiểu Chủ đề + Không đồng bộ

Tôi đoán đúng cách để thực hiện điều này và làm cho nó thực sự nhanh chóng bằng cách sử dụng kết nối không đồng bộ và kết nối với tất cả các URL. Đây có phải là cách tiếp cận đúng?

Ngoài ra, tôi đã cố gắng hiểu chủ đề và cách chúng hoạt động nhưng tôi dường như không thể hiểu được. Máy tính tôi đang sử dụng có bộ xử lý lõi tứ Intel Core i7-3610QM. Theo trang web của Intel cho các chi tiết kỹ thuật cho bộ xử lý này, nó có 8 chủ đề. Điều này có nghĩa là tôi có thể tạo 8 luồng trong một ứng dụng Java và tất cả chúng sẽ chạy đồng thời không? Bất kỳ hơn 8 và sẽ không có tăng tốc độ?

Chính xác số nào đại diện cho "Chủ đề" trong trình quản lý tác vụ trong tab "Hiệu suất"? Hiện tại, người quản lý tác vụ của tôi đang hiển thị "Chủ đề" là hơn 1.000. Tại sao nó là con số này và làm thế nào nó có thể thậm chí đi qua 8 nếu đó là tất cả các bộ vi xử lý của tôi hỗ trợ? Tôi cũng nhận thấy rằng khi tôi thử chương trình của tôi với 500 chủ đề như một bài kiểm tra, con số trong trình quản lý tác vụ tăng 500 nhưng nó có cùng tốc độ như tôi đặt nó để sử dụng 8 chủ đề thay thế. Vì vậy, nếu số lượng đang tăng theo số lượng chủ đề tôi đang sử dụng trong ứng dụng Java của tôi, thì tại sao tốc độ lại giống nhau?

Ngoài ra, tôi đã thử thực hiện một thử nghiệm nhỏ với các chuỗi trong Java nhưng đầu ra không có ý nghĩa với tôi. Đây là class Test của tôi:

import java.text.SimpleDateFormat; 
import java.util.Date; 

public class Test { 

    private static int numThreads = 3; 
    private static int numLoops = 100000; 
    private static SimpleDateFormat dateFormat = new SimpleDateFormat("[hh:mm:ss] "); 

    public static void main(String[] args) throws Exception { 

     for (int i=1; i<=numThreads; i++) { 
      final int threadNum = i; 
      new Thread(new Runnable() { 
       public void run() { 
        System.out.println(dateFormat.format(new Date()) + "Start of thread: " + threadNum); 
        for (int i=0; i<numLoops; i++) 
         for (int j=0; j<numLoops; j++); 
        System.out.println(dateFormat.format(new Date()) + "End of thread: " + threadNum); 
      } 
      }).start(); 
      Thread.sleep(2000); 
     } 

    } 
} 

Điều này tạo ra một sản lượng như:

[09:48:51] Start of thread: 1 
[09:48:53] Start of thread: 2 
[09:48:55] Start of thread: 3 
[09:48:55] End of thread: 3 
[09:48:56] End of thread: 1 
[09:48:58] End of thread: 2 

Tại sao khi bắt đầu chủ đề thứ ba và kết thúc ngay lập tức trong khi người đầu tiên và thứ hai mất mỗi 5 giây? Nếu tôi thêm nhiều hơn 3 chủ đề, cùng một điều xảy ra cho tất cả các chủ đề ở trên 2.

Xin lỗi nếu điều này đã được đọc lâu, tôi có rất nhiều câu hỏi. Cảm ơn trước.

Trả lời

9

Bộ xử lý của bạn có 8 lõi chứ không phải chủ đề. Điều này thực tế có nghĩa là chỉ có 8 thứ có thể chạy ở bất kỳ thời điểm nào. Điều đó không có nghĩa là bạn bị giới hạn chỉ có 8 luồng.

Khi một chuỗi đang mở đồng bộ một kết nối tới một URL, nó sẽ thường ngủ trong khi nó chờ máy chủ từ xa quay lại. Trong khi chủ đề đó đang ngủ các chủ đề khác có thể đang làm việc. Nếu bạn có 500 luồng và tất cả 500 luồng đang ngủ thì bạn không sử dụng bất kỳ lõi nào của CPU.

Mặt khác, nếu bạn có 500 chủ đề và tất cả 500 chủ đề muốn làm điều gì đó thì chúng không thể chạy cùng một lúc. Để xử lý kịch bản này, có một công cụ đặc biệt. Bộ vi xử lý (hoặc nhiều khả năng là hệ điều hành hoặc một số kết hợp của cả hai) có một bộ lập lịch để xác định các luồng nào sẽ chủ động chạy trên bộ xử lý tại bất kỳ thời điểm nào. Có nhiều quy tắc khác nhau và đôi khi hoạt động ngẫu nhiên kiểm soát cách các trình lập lịch này hoạt động. Điều này có thể giải thích tại sao trong ví dụ trên, chuỗi 3 luôn có vẻ như kết thúc trước. Có lẽ bộ lập lịch thích chuỗi 3 vì nó là luồng mới nhất được sắp xếp theo luồng chính, đôi khi nó không thể dự đoán được hành vi.

Bây giờ để trả lời câu hỏi của bạn về hiệu suất.Nếu mở một kết nối không bao giờ liên quan đến một giấc ngủ thì nó sẽ không quan trọng nếu bạn đang xử lý mọi thứ đồng bộ hoặc không đồng bộ, bạn sẽ không thể nhận được bất kỳ đạt được hiệu suất trên 8 chủ đề. Trong thực tế, rất nhiều thời gian liên quan đến việc mở một kết nối được chi tiêu ngủ. Sự khác biệt giữa không đồng bộ và đồng bộ là cách xử lý thời gian đó để ngủ. Về mặt lý thuyết bạn sẽ có thể có được hiệu suất gần như bằng nhau giữa hai người.

Với mô hình đa luồng, bạn chỉ cần tạo nhiều chuỗi hơn là có lõi. Khi các chủ đề nhấn một giấc ngủ họ để cho các chủ đề khác làm việc. Điều này đôi khi có thể dễ dàng hơn để xử lý bởi vì bạn không phải viết bất kỳ lịch trình hoặc tương tác giữa các chủ đề.

Với mô hình không đồng bộ, bạn chỉ tạo một chuỗi cho mỗi lõi. Nếu thread đó cần ngủ thì nó không ngủ nhưng thực sự phải có mã để xử lý việc chuyển sang kết nối tiếp theo. Ví dụ, giả sử có ba bước trong mở một kết nối (A, B, C):

while (!connectionsList.isEmpty()) { 
    for(Connection connection : connectionsList) { 

    if connection.getState() == READY_FOR_A { 
     connection.stepA(); 
     //this method should return immediately and the connection 
     //should go into the waiting state for some time before going 
     //into the READY_FOR_B state 
    } 
    if connection.getState() == READY_FOR_B { 
     connection.stepB(); 
     //same immediate return behavior as above 
    } 
    if connection.getState() == READY_FOR_C { 
     connection.stepC(); 
     //same immediate return behavior as above 
    } 
    if connection.getState() == WAITING { 
     //Do nothing, skip over 
    } 
    if connection.getState() == FINISHED { 
     connectionsList.remove(connection); 
    } 
    } 
} 

Chú ý rằng tại không có điểm nào ngủ chủ đề như vậy không có điểm trong có nhiều chủ đề hơn bạn có lõi. Cuối cùng, cho dù đi với một cách tiếp cận đồng bộ hoặc một cách tiếp cận không đồng bộ là một vấn đề sở thích cá nhân. Chỉ ở những thái cực tuyệt đối sẽ có sự khác biệt về hiệu suất giữa hai yếu tố này và bạn sẽ cần phải dành một thời gian dài để định hình đến điểm mà đó là nút cổ chai trong ứng dụng của bạn.

Có vẻ như bạn đang tạo nhiều chủ đề và không nhận được bất kỳ lợi ích nào về hiệu suất. Có thể có một số lý do cho việc này.

  • Có thể thiết lập kết nối của bạn không thực sự ngủ trong trường hợp này tôi không mong đợi đạt được hiệu suất đạt được trong 8 chủ đề. Tôi không nghĩ rằng điều này là có khả năng.
  • Có thể tất cả các chủ đề đang sử dụng một số tài nguyên được chia sẻ chung. Trong trường hợp này, các luồng khác không thể làm việc vì luồng ngủ có tài nguyên được chia sẻ. Có bất kỳ đối tượng nào mà tất cả các chủ đề chia sẻ không? Đối tượng này có bất kỳ phương thức đồng bộ nào không?
  • Có thể bạn đã đồng bộ hóa riêng. Điều này có thể tạo ra vấn đề nêu trên.
  • Có thể mỗi chủ đề phải thực hiện một số loại công việc thiết lập/phân bổ đang đánh bại lợi ích bạn đang đạt được bằng cách sử dụng nhiều luồng.

Nếu tôi là bạn, tôi sẽ sử dụng một công cụ như JVisualVM để cấu hình ứng dụng của bạn khi chạy với một số lượng nhỏ chủ đề (20). JVisualVM có một biểu đồ chuỗi màu tốt đẹp sẽ hiển thị khi các luồng đang chạy, chặn hoặc ngủ. Điều này sẽ giúp bạn hiểu mối quan hệ thread/core như bạn sẽ thấy rằng số lượng các chủ đề đang chạy nhỏ hơn số lõi bạn có. Ngoài ra, nếu bạn thấy nhiều chủ đề bị chặn thì có thể dẫn bạn đến nút cổ chai của bạn (nếu bạn thấy nhiều chủ đề bị chặn sử dụng JVisualVM để tạo một chuỗi kết xuất tại thời điểm đó và xem chủ đề bị chặn).

+0

Cảm ơn bạn đã trả lời. Chỉnh sửa ... – user1203585

+0

Ahh, tôi không thể chỉnh sửa nhận xét đó thực sự ... giới hạn 5 phút ... "Có đối tượng nào mà tất cả các chuỗi chia sẻ không? Đối tượng này có bất kỳ phương pháp đồng bộ nào không?" Tất cả các chủ đề của tôi đang thực hiện tương tự: Nó khởi tạo đối tượng URL và mở kết nối với proxy. Nó đặt URLConnection kết nối và đọc hết thời gian chờ. Sau đó, nó sử dụng một BufferedReader và InputStreamReader để đọc từ URLConnection. Cuối cùng, nó viết một từ vào một tập tin văn bản. Đó là những gì mỗi chủ đề đang làm và chạy 500 trong số các chủ đề này dường như không tăng tốc độ:/ – user1203585

+1

Tôi đã thực hiện một số việc đào bới. Tôi nghi ngờ rằng Java có một hồ bơi kết nối cơ bản được giới hạn về kích thước. Có một thuộc tính mạng có tên http.maxConnections [xem tại đây] (http://docs.oracle.com/javase/1.4.2/docs/guide/net/properties.html). Mặc định là 5. Điều này có nghĩa là sau khi bạn có nhiều hơn 5 kết nối mở, tất cả chúng đều sử dụng cùng 5 ổ cắm cơ bản (tài nguyên được chia sẻ) và bất kỳ kết nối nào bạn mở sau đó sẽ chặn. Một lần nữa, bạn có thể sử dụng JVisualVM để xác nhận điều này. – Pace

1

Một số khái niệm:

Bạn có thể có nhiều chủ đề trong hệ thống, nhưng chỉ có một số trong số họ (tối đa 8 trong trường hợp của bạn) sẽ được "lên kế hoạch" trên CPU tại bất kỳ thời điểm. Vì vậy, bạn không thể có được hiệu suất cao hơn 8 chủ đề chạy song song. Trong thực tế, hiệu suất có thể sẽ giảm xuống khi bạn tăng số lượng các chủ đề, bởi vì công việc liên quan đến việc tạo, hủy và quản lý các luồng.

Chủ đề có thể ở các trạng thái khác nhau: http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.State.html Trong số các trạng thái đó, các chuỗi RUNNABLE đứng để lấy một lát thời gian CPU. Hệ điều hành quyết định gán thời gian CPU cho các luồng. Trong một hệ thống thông thường với 1000 chủ đề, nó có thể hoàn toàn không thể đoán trước khi một chuỗi nhất định sẽ nhận được thời gian CPU và thời gian nó sẽ ở trên CPU.

Về vấn đề bạn đang giải quyết:

Bạn dường như đã tìm ra giải pháp đúng - làm cho các yêu cầu mạng không đồng bộ song song. Tuy nhiên, thực tế nói bắt đầu từ 10000 chủ đề và nhiều kết nối mạng, đồng thời, có thể là một sự căng thẳng về tài nguyên hệ thống và nó có thể không hoạt động. Điều này post có nhiều đề xuất cho I/O không đồng bộ bằng cách sử dụng Java. (Mẹo: Đừng chỉ nhìn vào câu trả lời được chấp nhận)

0

Giải pháp này cụ thể hơn cho vấn đề chung khi cố gắng thực hiện yêu cầu 10k nhanh nhất có thể. Tôi khuyên bạn nên bỏ qua các thư viện Java HTTP và sử dụng Apache HttpClient để thay thế. Họ có một số suggestions để tối đa hóa hiệu suất có thể hữu ích. Tôi đã nghe thư viện Apache HttpClient chỉ nhanh hơn nói chung là tốt, trọng lượng nhẹ hơn và chi phí thấp hơn.

Các vấn đề liên quan