Cara memulai dua utas pada waktu yang "tepat"

92

Utas harus dimulai pada sepersekian detik yang sama. Saya mengerti, jika Anda melakukannya thread1.start(), diperlukan beberapa milidetik sebelum eksekusi berikutnya thread2.start().

Apakah itu mungkin atau tidak mungkin?

figaro
sumber
2
Sepersekian detik adalah waktu yang sangat lama pada kecepatan GHz.
Nikolai Fetissov
32
Tidak perlu terlalu sering memberi suara negatif. Tidak semua orang memahami nondeterminisme terkait threading, dan kita semua harus mulai dari suatu tempat.
Michael Petrotta
7
Tidak mengerti suara negatifnya. Sinkronisasi antar utas adalah kebutuhan yang sangat umum. Ya, di java Anda tidak dapat menjalankannya persis secara paralel (yang, di platform lain dapat menjadi persyaratan yang sangat valid btw), tetapi dari waktu ke waktu, sangat umum Anda perlu menyinkronkan tindakan mereka. Itulah mengapa jdk memiliki kelas untuk melakukan itu. Mungkin kata-katanya tidak akurat, tapi neraka jika dia tahu itu, dia tidak akan mengajukan pertanyaan ..
Enno Shioji
baiklah, saya rasa saya mengerti semua kemarahan Anda. Itu adalah pertanyaan yang diajukan kepada saya dalam sebuah wawancara ... mungkin adalah tipuan Q. Tapi, saya hanya bingung dan ingin memastikannya. itu sebabnya saya bertanya apakah itu mungkin.
figaro
2
@javaguy - bukan pertanyaan "tipuan". Agak pertanyaan yang dipilih untuk melihat seberapa baik Anda benar-benar memahami pemrograman multi-threaded secara umum ... juga dalam kasus Java.
Stephen C

Jawaban:

136

Untuk memulai utas pada waktu yang persis sama (setidaknya sebaik mungkin), Anda dapat menggunakan 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");

Ini tidak harus berupa CyclicBarrier, Anda juga dapat menggunakan CountDownLatchatau bahkan kunci.

Ini masih tidak dapat memastikan bahwa mereka dimulai tepat pada waktu yang sama pada JVM standar, tetapi Anda bisa cukup mendekati. Mendekatkan diri masih berguna saat Anda melakukan, misalnya, uji kinerja. Misalnya, jika Anda mencoba mengukur throughput dari struktur data dengan jumlah thread berbeda yang memukulnya, Anda ingin menggunakan jenis konstruksi ini untuk mendapatkan hasil yang paling akurat.

Di platform lain, memulai utas dengan tepat bisa menjadi persyaratan yang sangat valid.

Enno Shioji
sumber
5
+1 pemikiran yang bagus ;-) Meskipun tentu saja itu tidak membuat utas mulai tepat pada waktu yang sama dalam waktu nyata (setidaknya tidak pada chip inti tunggal, dan tidak dijamin bahkan pada chip multi-inti), tetapi Saya tidak bisa memikirkan cara untuk berbuat lebih baik.
David Z
Akankah kait ini masuk ke dalam pegangan tunggu khusus lingkungan?
ChaosPandion
@ChaosPandion: Peduli untuk menguraikan?
Santa
@Santa - Win32 API misalnya menawarkan primitif yang berbeda. Salah satu jenis yang berguna adalah acara reset manual yang dikembalikan saat Anda menelepon CreateEvent. msdn.microsoft.com/en-us/library/ms686364%28VS.85%29.aspx
ChaosPandion
1
@ Zwei - Apapun itu, ini adalah jawaban yang akan saya posting seandainya saya menjadi seorang guru Java.
ChaosPandion
15

Ini tidak mungkin, setidaknya pada satu komputer inti. Tapi kenapa kamu menginginkan itu? Meskipun Anda dapat memulai dua utas pada detik yang persis sama, kemajuannya akan berbeda karena penjadwalan tidak ada dalam kendali Anda.

Sunting: (Menanggapi beberapa komentar) Ini adalah persyaratan yang sangat valid untuk menyinkronkan keadaan atau kemajuan beberapa utas dan CyclicBarriermerupakan alat yang hebat. Saya menjawab pertanyaan apakah mungkin untuk memulai beberapa utas pada waktu yang persis sama . CyclicBarrierakan menjamin bahwa utas melanjutkan ketika mereka berada tepat pada keadaan yang diinginkan tetapi itu tidak menjamin bahwa utas akan memulai atau melanjutkan pada waktu yang persis sama, meskipun mungkin cukup dekat. Tidak disebutkan kebutuhan sinkronisasi dalam pertanyaan.

samitgaur
sumber
1
Anda bisa sangat dekat. Itu semua tergantung teknik sinkronisasi yang Anda gunakan dan tentunya memiliki lebih dari 1 cpu atau core.
ChaosPandion
Idenya adalah bahwa ini adalah pendekatan yang salah jalan dan seharusnya tidak diperlukan dalam lingkungan yang tidak-sulit-real-time, bukan karena itu "cukup dekat".
Nikolai Fetissov
1
Ini juga tidak mungkin karena penjadwalan thread Java tidak memberi Anda akurasi milidetik.
Stephen C
1
@ Zwei - Anda mungkin bisa "sangat dekat" sebagian besar waktu pada mesin yang tidak aktif. Tetapi jika Anda membutuhkannya sepanjang waktu, Anda perlu memprogram dalam bahasa seperti C, pada mesin dengan dukungan waktu nyata yang keras di OS. Pertimbangkan juga bahwa JVM mungkin menjalankan GC penuh pada "sepersekian detik" yang Anda inginkan untuk memulai utas.
Stephen C
1
Ini adalah masalah sinkronisasi yang benar-benar valid. Thread perlu menginisialisasi beberapa data, pastikan tidak ada yang masuk ke region kritis tanpa validasi yang tepat. Dan fakta bahwa CyclicBerrier termasuk dalam paket konkuren javas berarti ini masalah yang penting.
Denis Tulskiy
14

Anda bisa menggunakan CountDownLatch untuk ini. Temukan di bawah contoh. Meskipun t1 dan t2 dimulai, utas ini terus menunggu hingga utas utama menghitung mundur kait. Jumlah hitungan mundur yang dibutuhkan disebutkan dalam konstruktor. Kait penghitung mundur juga dapat digunakan untuk menunggu utas menyelesaikan eksekusi sehingga utas utama dapat melanjutkan lebih jauh (kasus sebaliknya). Kelas ini termasuk sejak 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
    }
}
aNish
sumber
Jawaban ini bisa mendapatkan keuntungan dari lebih sedikit boilerplate dalam contoh kode.
pengguna2418306
6
  1. Seperti yang saya pahami, JVM sebagian besar mendelegasikan hal ini ke sistem operasi. Jadi jawabannya akan spesifik untuk OS.
  2. Ini jelas tidak mungkin dilakukan pada mesin prosesor tunggal.
  3. Ini lebih rumit sehubungan dengan mesin multi-prosesor. Menurut Relativitas simultanitas , "tidak mungkin untuk mengatakan secara absolut apakah dua peristiwa terjadi pada saat yang sama jika peristiwa itu terpisah dalam ruang." Tidak peduli seberapa dekat prosesor Anda, mereka dipisahkan dalam ruang.
    1. Jika Anda dapat menerima keserempakan relatif, maka mungkin lebih mudah untuk mensimulasikannya menggunakan teknik yang dibahas dalam tanggapan lain.
emory
sumber
1
… Dan bahkan jika kita mengasumsikan permulaan simultan, setiap utas memiliki garis waktunya sendiri, semuanya bersamaan , tetapi tidak harus paralel, jadi apa pun yang diasumsikan seseorang tentang simultanitas utas dapat (akan) batal dalam nanodetik berikutnya (dari yang mana saja timeline)…
Holger