Cách bắt đầu hai chủ đề “chính xác” cùng một lúc


91

Các chủ đề sẽ bắt đầu trong cùng một giây. Tôi hiểu, nếu bạn làm vậy thread1.start(), sẽ mất vài mili giây trước khi thực hiện tiếp theo thread2.start().

Nó thậm chí có thể hoặc không thể?


2
Tách giây là một khoảng thời gian rất dài ở tốc độ GHz.
Nikolai Fetissov,

30
Không cần phải phản đối dữ dội như vậy. Không phải ai cũng hiểu chủ nghĩa không xác định liên quan đến luồng, và tất cả chúng ta đều phải bắt đầu từ đâu đó.
Michael Petrotta,

7
Không hiểu các phiếu phản đối. Đồng bộ hóa giữa các luồng là một nhu cầu rất phổ biến. Có trong java, bạn không thể để chúng thực thi chính xác song song (điều này, trên nền tảng khác có thể là một yêu cầu rất hợp lệ btw), nhưng đôi khi bạn cần phải đồng bộ hóa hành động của chúng. Đó là lý do tại sao jdk có các lớp để làm điều đó. Có lẽ cách diễn đạt là không chính xác, nhưng địa ngục nếu anh biết rằng, anh sẽ không hỏi những câu ..
Enno Shioji

tốt, tôi đoán tôi hiểu tất cả sự tức giận của bạn. Đó là một câu hỏi được đặt ra cho tôi trong một cuộc phỏng vấn ... có lẽ là một trò lừa Q. Nhưng, tôi chỉ thấy bối rối và muốn xác nhận nó. đó là lý do tại sao tôi hỏi liệu nó có khả thi không.
figaro,

2
@javaguy - không phải là một câu hỏi "lừa". Thay vào đó là một câu hỏi được chọn để xem bạn thực sự hiểu như thế nào về lập trình đa luồng nói chung ... cũng như trong trường hợp Java.
Stephen C,

Câu trả lời:


135

Để bắt đầu các chuỗi chính xác cùng một lúc (ít nhất càng tốt càng tốt), bạn có thể sử dụng CyclicBarrier :

// We want to start just 2 threads at the same time, but let's control that 
// timing from the main thread. That's why we have 3 "parties" instead of 2.
final CyclicBarrier gate = new CyclicBarrier(3);

Thread t1 = new Thread(){
    public void run(){
        gate.await();
        //do stuff    
    }};
Thread t2 = new Thread(){
    public void run(){
        gate.await();
        //do stuff    
    }};

t1.start();
t2.start();

// At this point, t1 and t2 are blocking on the gate. 
// Since we gave "3" as the argument, gate is not opened yet.
// Now if we block on the gate from the main thread, it will open
// and all threads will start to do stuff!

gate.await();
System.out.println("all threads started");

Điều này không nhất thiết phải là một CyclicBarrier, bạn cũng có thể sử dụng một CountDownLatchhoặc thậm chí là một ổ khóa.

Điều này vẫn không thể đảm bảo rằng chúng được khởi động chính xác cùng lúc trên các JVM tiêu chuẩn, nhưng bạn có thể đến khá gần. Đến gần vẫn hữu ích khi bạn thực hiện các bài kiểm tra hiệu suất, ví dụ. Ví dụ: nếu bạn đang cố gắng đo lường thông lượng của một cấu trúc dữ liệu với số lượng luồng khác nhau chạm vào nó, bạn muốn sử dụng loại cấu trúc này để có được kết quả chính xác nhất có thể.

Trên các nền tảng khác, bắt đầu chủ đề chính xác có thể là một yêu cầu rất hợp lệ btw.


5
+1 suy nghĩ tốt đẹp ;-) Mặc dù tất nhiên nó không làm cho các luồng bắt đầu chính xác cùng một lúc trong thời gian thực (ít nhất là không phải trên chip đơn lõi và không được đảm bảo ngay cả trên chip đa lõi), nhưng Tôi không thể nghĩ ra cách nào để làm tốt hơn.
David Z,

Điều này sẽ kết nối với các xử lý chờ môi trường cụ thể?
ChaosPandion

@ChaosPandion: Chăm sóc tỉ mỉ?
Ông già Noel,

@Santa - Ví dụ: API Win32 cung cấp các nguyên thủy khác nhau. Một loại hữu ích là sự kiện đặt lại thủ công được trả lại khi bạn gọi CreateEvent. msdn.microsoft.com/en-us/library/ms686364%28VS.85%29.aspx
ChaosPandion

1
@Zwei - Chà, dù là gì đi nữa, thì đây là câu trả lời tôi đã đăng nếu tôi là một chuyên gia Java.
ChaosPandion

15

Điều này là không thể, ít nhất là trên một máy tính lõi đơn. Nhưng tại sao bạn muốn điều đó? Ngay cả khi bạn có thể bắt đầu hai luồng chính xác trong cùng một giây, chúng sẽ tiến triển khác nhau vì việc lập lịch không nằm trong tầm kiểm soát của bạn.

Chỉnh sửa: (Đáp lại một số ý kiến) Đây là một yêu cầu hoàn toàn hợp lệ để đồng bộ hóa trạng thái hoặc tiến trình của nhiều luồng và CyclicBarrierlà một công cụ tuyệt vời. Tôi đã trả lời câu hỏi liệu có thể bắt đầu nhiều chủ đề cùng một lúc không . CyclicBarriersẽ đảm bảo rằng các luồng tiếp tục khi chúng ở chính xác ở trạng thái mong muốn nhưng nó không đảm bảo rằng chúng sẽ bắt đầu hoặc tiếp tục chính xác cùng một lúc, mặc dù nó có thể khá gần. Không có đề cập đến nhu cầu đồng bộ hóa trong câu hỏi.


1
Bạn có thể nhận được khá gần mặc dù. Tất cả phụ thuộc vào kỹ thuật đồng bộ hóa mà bạn sử dụng và tất nhiên có nhiều hơn 1 cpu hoặc lõi.
ChaosPandion

Ý kiến ​​cho rằng đây là cách tiếp cận sai lầm và không cần thiết trong bất kỳ môi trường thời gian thực không khó, không phải là nó "khá gần".
Nikolai Fetissov,

1
Đó là cũng không thể vì Java chủ đề lịch trình không cung cấp cho bạn chính xác phần nghìn giây.
Stephen C,

1
@Zwei - bạn có thể bị "chết tiệt" hầu hết thời gian trên một máy không hoạt động. Nhưng nếu bạn luôn cần nó, bạn cần phải lập trình bằng ngôn ngữ như C, trên một máy có hỗ trợ thời gian thực cứng trong HĐH. Cũng nên xem xét rằng JVM có thể đang chạy một GC đầy đủ vào "giây phút" mà bạn muốn bắt đầu chuỗi của mình.
Stephen C,

1
Đó là một vấn đề đồng bộ hóa hoàn toàn hợp lệ. Các luồng cần khởi tạo một số dữ liệu, đảm bảo rằng không ai đi vào vùng quan trọng mà không được xác thực thích hợp. Và thực tế là CyclicBerrier được bao gồm trong gói đồng thời javas có nghĩa rằng đây là một vấn đề quan trọng.
Denis Tulskiy,

14

Bạn có thể sử dụng CountDownLatch cho việc này. Vui lòng tìm dưới đây một mẫu. Mặc dù t1 và t2 được khởi động, các luồng này vẫn tiếp tục đợi cho đến khi luồng chính đếm ngược chốt. Số lần đếm ngược yêu cầu được đề cập trong hàm tạo. Chốt đếm ngược cũng có thể được sử dụng để đợi các luồng kết thúc quá trình thực thi để luồng chính có thể tiến hành thêm (trường hợp ngược lại). Lớp này đã được đưa vào từ Java 1.5.

import java.util.concurrent.CountDownLatch;


public class ThreadExample
{
    public static void main(String[] args) 
    {
        CountDownLatch latch = new CountDownLatch(1);
        MyThread t1 = new MyThread(latch);
        MyThread t2 = new MyThread(latch);
        new Thread(t1).start();
        new Thread(t2).start();
        //Do whatever you want
        latch.countDown();          //This will inform all the threads to start
        //Continue to do whatever
    }
}

class MyThread implements Runnable
{
    CountDownLatch latch;
    public MyThread(CountDownLatch latch) 
    {
        this.latch = latch;
    }
    @Override
    public void run() 
    {
        try 
        {
            latch.await();          //The thread keeps waiting till it is informed
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Do the actual thing
    }
}

Câu trả lời này có thể được hưởng lợi từ ít bản viết sẵn hơn trong ví dụ mã.
user2418306

6
  1. Theo tôi hiểu, JVM chủ yếu ủy thác công cụ này cho hệ điều hành. Vì vậy, câu trả lời sẽ dành riêng cho hệ điều hành.
  2. Điều này rõ ràng là không thể trên các máy một bộ xử lý.
  3. Nó phức tạp hơn đối với máy nhiều bộ xử lý. Theo Thuyết tương đối của tính đồng thời , "không thể nói theo nghĩa tuyệt đối liệu hai sự kiện có xảy ra cùng một lúc hay không nếu những sự kiện đó tách rời nhau trong không gian." Cho dù các bộ xử lý của bạn có gần nhau đến đâu, chúng vẫn được tách biệt trong không gian.
    1. Nếu bạn có thể chấp nhận tính đồng thời tương đối, thì việc mô phỏng nó bằng các kỹ thuật được thảo luận trong các phản hồi khác có lẽ dễ dàng hơn.

1
… Và ngay cả khi chúng ta giả sử bắt đầu đồng thời, mỗi luồng có dòng thời gian riêng của nó, tất cả chúng đều đồng thời , nhưng không nhất thiết phải song song, vì vậy bất cứ điều gì ai đó giả định về tính đồng thời của các luồng có thể (sẽ) bị vô hiệu trong giây nano tiếp theo (bất kỳ điều nào dòng thời gian)…
Holger
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.