JavaRush /Java Blog /Random-ID /Anda tidak dapat merusak Java dengan Thread: Bagian VI - ...
Viacheslav
Level 3

Anda tidak dapat merusak Java dengan Thread: Bagian VI - Menuju penghalang!

Dipublikasikan di grup Random-ID

Perkenalan

Streaming adalah hal yang menarik. Dalam ulasan sebelumnya, kita melihat beberapa alat yang tersedia untuk mengimplementasikan multithreading. Mari kita lihat hal menarik apa lagi yang bisa kita lakukan. Saat ini kita tahu banyak. Misalnya, dari “ Anda Tidak Dapat Memanjakan Java dengan Thread: Bagian I - Threads, ” kita tahu bahwa thread adalah Thread. Kita tahu bahwa thread sedang melakukan beberapa tugas. Jika kita ingin tugas kita dapat dijalankan ( run), maka kita harus menentukan threadnya sebagai Runnable. Anda tidak dapat merusak Java dengan Thread: Bagian VI - Menuju penghalang!  - 1Yang perlu diingat, kita bisa menggunakan Tutorialspoint Java Online Compiler :
public static void main(String []args){
	Runnable task = () -> {
 		Thread thread = Thread.currentThread();
		System.out.println("Hello from " + thread.getName());
	};
	Thread thread = new Thread(task);
	thread.start();
}
Kita juga tahu bahwa kita mempunyai konsep seperti kunci. Kita membacanya di “ Anda Tidak Dapat Memanjakan Java dengan Thread: Bagian II - Sinkronisasi .” Sebuah thread dapat menempati sebuah kunci dan kemudian thread lain yang mencoba menempati kunci tersebut akan terpaksa menunggu hingga kunci tersebut menjadi bebas:
import java.util.concurrent.locks.*;

public class HelloWorld{
	public static void main(String []args){
		Lock lock = new ReentrantLock();
		Runnable task = () -> {
			lock.lock();
			Thread thread = Thread.currentThread();
			System.out.println("Hello from " + thread.getName());
			lock.unlock();
		};
		Thread thread = new Thread(task);
		thread.start();
	}
}
Saya pikir ini saatnya membicarakan hal menarik lainnya yang bisa kita lakukan.

Semafor

Cara paling sederhana untuk mengontrol berapa banyak thread yang dapat bekerja secara bersamaan adalah semaphore. Seperti di rel kereta api. Lampu hijau menyala - Anda bisa. Lampu merah menyala - kami menunggu. Apa yang kita harapkan dari semaphore? Izin. Izin dalam bahasa Inggris - izin. Untuk mendapatkan izin, Anda perlu mendapatkannya, yang dalam bahasa Inggris akan diperoleh. Dan bila izin itu sudah tidak diperlukan lagi, maka kita harus memberikannya, yaitu melepaskan atau membuangnya, yang dalam bahasa Inggris disebut pelepasan. Mari kita lihat cara kerjanya. Kita perlu mengimpor kelas java.util.concurrent.Semaphore. Contoh:
public static void main(String[] args) throws InterruptedException {
	Semaphore semaphore = new Semaphore(0);
	Runnable task = () -> {
		try {
			semaphore.acquire();
			System.out.println("Finished");
			semaphore.release();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	Thread.sleep(5000);
	semaphore.release(1);
}
Seperti yang bisa kita lihat, setelah menghafal kata-kata bahasa Inggris, kita memahami cara kerja semaphore. Menariknya, syarat utamanya adalah “akun” semaphore tersebut harus memiliki jumlah izin yang positif. Oleh karena itu, Anda dapat memulainya dengan nilai minus. Dan Anda dapat meminta (memperoleh) lebih dari 1.

Hitung MundurLatch

Mekanisme selanjutnya adalah CountDownLatch. CountDown dalam bahasa Inggris adalah countdown, dan Latch adalah baut atau latch. Artinya, jika kita terjemahkan, maka ini adalah kait dengan hitungan mundur. Di sini kita memerlukan impor kelas yang sesuai java.util.concurrent.CountDownLatch. Ibarat perlombaan atau perlombaan dimana semua orang berkumpul di garis start dan ketika semua orang sudah siap, izin diberikan dan semua orang memulai pada waktu yang sama. Contoh:
public static void main(String[] args) {
	CountDownLatch countDownLatch = new CountDownLatch(3);
	Runnable task = () -> {
		try {
			countDownLatch.countDown();
			System.out.println("Countdown: " + countDownLatch.getCount());
			countDownLatch.await();
			System.out.println("Finished");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	for (int i = 0; i < 3; i++) {
		new Thread(task).start();
 	}
}
menunggu dalam bahasa Inggris - mengharapkan. Artinya, kita bicara dulu countDown. Seperti yang dikatakan Google Translator, hitung mundur adalah “tindakan menghitung angka dalam urutan terbalik ke nol”, yaitu melakukan tindakan hitung mundur, yang tujuannya adalah menghitung sampai nol. Dan kemudian kita katakan await- yaitu, tunggu sampai nilai penghitungnya menjadi nol. Sangat menarik bahwa penghitung seperti itu dapat dibuang. Seperti yang dikatakan di JavaDoc - "Ketika utas harus berulang kali menghitung mundur dengan cara ini, gunakan CyclicBarrier sebagai gantinya", yaitu, jika Anda memerlukan penghitungan yang dapat digunakan kembali, Anda perlu menggunakan opsi lain, yang disebut CyclicBarrier.

Penghalang Siklik

Seperti namanya, CyclicBarrierini adalah penghalang siklus. Kita perlu mengimpor kelas java.util.concurrent.CyclicBarrier. Mari kita lihat sebuah contoh:
public static void main(String[] args) throws InterruptedException {
	Runnable action = () -> System.out.println("На старт!");
	CyclicBarrier berrier = new CyclicBarrier(3, action);
	Runnable task = () -> {
		try {
			berrier.await();
			System.out.println("Finished");
		} catch (BrokenBarrierException | InterruptedException e) {
			e.printStackTrace();
		}
	};
	System.out.println("Limit: " + berrier.getParties());
	for (int i = 0; i < 3; i++) {
		new Thread(task).start();
	}
}
Seperti yang Anda lihat, thread sedang dieksekusi await, yaitu menunggu. Dalam hal ini, nilai penghalangnya berkurang. Penghalang dianggap rusak ( berrier.isBroken()) ketika hitungan mundur mencapai nol. Untuk mengatur ulang penghalang, Anda perlu menelepon berrier.reset(), yang tidak ada di CountDownLatch.

Penukar

Obat selanjutnya adalah Exchanger. Exchange dari bahasa Inggris diterjemahkan sebagai exchange atau pertukaran. A Exchangeradalah penukar, yaitu sesuatu yang melaluinya mereka bertukar. Mari kita lihat contoh sederhana:
public static void main(String[] args) {
	Exchanger<String> exchanger = new Exchanger<>();
	Runnable task = () -> {
		try {
			Thread thread = Thread.currentThread();
			String withThreadName = exchanger.exchange(thread.getName());
			System.out.println(thread.getName() + " обменялся с " + withThreadName);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	new Thread(task).start();
}
Di sini kami meluncurkan dua utas. Masing-masing mengeksekusi metode pertukaran dan menunggu thread lain juga menjalankan metode pertukaran. Dengan demikian, thread akan bertukar argumen yang disampaikan satu sama lain. Hal yang menarik. Apa dia tidak mengingatkanmu pada sesuatu? Dan beliau mengingatkan SynchronousQueue, yang terletak pada inti cachedThreadPool'a. Agar lebih jelas, berikut contohnya:
public static void main(String[] args) throws InterruptedException {
	SynchronousQueue<String> queue = new SynchronousQueue<>();
	Runnable task = () -> {
		try {
			System.out.println(queue.take());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	queue.put("Message");
}
Contoh menunjukkan bahwa dengan meluncurkan thread baru, thread ini akan masuk ke mode tunggu, karena antrian akan kosong. Dan kemudian mainthread akan memasukkan teks “Pesan” ke dalam antrian. Pada saat yang sama, ini akan berhenti selama waktu yang diperlukan hingga elemen teks ini diterima dari antrian. Pada topik ini Anda juga dapat membaca " SynchronousQueue Vs Exchanger ".

Faser

Dan akhirnya, hal yang paling manis - Phaser. Kita perlu mengimpor kelas java.util.concurrent.Phaser. Mari kita lihat contoh sederhana:
public static void main(String[] args) throws InterruptedException {
        Phaser phaser = new Phaser();
        // Вызывая метод register, мы регистрируем текущий поток (main) How участника
        phaser.register();
        System.out.println("Phasecount is " + phaser.getPhase());
        testPhaser(phaser);
        testPhaser(phaser);
        testPhaser(phaser);
        // Через 3 секунды прибываем к барьеру и снимаемся регистрацию. Кол-во прибывших = кол-во регистраций = пуск
        Thread.sleep(3000);
        phaser.arriveAndDeregister();
        System.out.println("Phasecount is " + phaser.getPhase());
    }

    private static void testPhaser(final Phaser phaser) {
        // Говорим, что будет +1 участник на Phaser
        phaser.register();
        // Запускаем новый поток
        new Thread(() -> {
            String name = Thread.currentThread().getName();
            System.out.println(name + " arrived");
            phaser.arriveAndAwaitAdvance(); //threads register arrival to the phaser.
            System.out.println(name + " after passing barrier");
        }).start();
    }
Contoh menunjukkan bahwa penghalang ketika menggunakan Phaser'a akan rusak ketika jumlah pendaftaran bertepatan dengan jumlah kedatangan di penghalang. Anda dapat mengetahui lebih lanjut Phaserdi artikel dari hub " Sinkronisasi Phaser Baru ".

Hasil

Seperti yang Anda lihat dari contoh, ada berbagai cara untuk menyinkronkan thread. Tadi saya mencoba mengingat sesuatu tentang multithreading, semoga bagian sebelumnya bermanfaat. Mereka mengatakan bahwa jalan menuju multithreading dimulai dengan buku "Java Concurrency in Practice". Meski terbit pada tahun 2006, masyarakat menanggapi bahwa buku tersebut cukup mendasar dan tetap menarik. Misalnya, Anda dapat membaca diskusi di sini: " Apakah Java Concurrency In Practice masih valid? ". Membaca tautan dari diskusi juga bermanfaat. Misalnya, ada link ke buku " Pengembang Java yang Beralas Baik ", yang patut diperhatikan" Bab 4. Konkurensi modern ". Ada ulasan lengkap lainnya dengan topik yang sama: “ Apakah cocurrency Java dalam praktiknya masih relevan di era java 8 ”. Ini juga berisi tip tentang apa lagi yang harus Anda baca untuk benar-benar memahami topiknya. Setelah itu, Anda dapat melihat lebih dekat buku yang luar biasa seperti " Tes Latihan Pemrogram OCA OCP JavaSE 8 ". Kami tertarik dengan bagian kedua, yaitu OCP. Dan ada tes di "∫". Buku ini berisi pertanyaan dan jawaban beserta penjelasannya. Misalnya: Anda tidak dapat merusak Java dengan Thread: Bagian VI - Menuju penghalang!  - 3Banyak orang mungkin mulai mengatakan bahwa ini hanyalah metode menghafal lainnya. Di satu sisi, ya. Di sisi lain, pertanyaan ini dapat dijawab dengan mengingat bahwa ExecutorServiceini adalah semacam “peningkatan” Executor. Dan Executorini dimaksudkan hanya untuk menyembunyikan metode pembuatan thread, tetapi bukan cara utama mengeksekusinya, yaitu berjalan di thread baru Runnable. Oleh karena itu, execute(Callable)tidak, karena mereka hanya menambahkan metode ExecutorServiceyang dapat mengembalikan . Seperti yang Anda lihat, kita dapat menghafal daftar metode, tetapi akan lebih mudah untuk menebaknya jika kita mengetahui sifat dari kelas itu sendiri. Nah, beberapa materi tambahan tentang topik ini: ExecutorsubmitFuture #Viacheslav
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION