JavaRush /Blog Java /Random-MS /Anda tidak boleh merosakkan Java dengan Thread: Bahagian ...

Anda tidak boleh merosakkan Java dengan Thread: Bahagian VI - Ke penghalang!

Diterbitkan dalam kumpulan

pengenalan

Aliran adalah perkara yang menarik. Dalam ulasan sebelumnya, kami melihat beberapa alat yang tersedia untuk melaksanakan multithreading. Mari lihat apa lagi perkara menarik yang boleh kita lakukan. Pada ketika ini kita tahu banyak. Contohnya, daripada " Anda Tidak Boleh Merosakkan Java dengan Benang: Bahagian I - Benang, " kita tahu bahawa benang ialah Benang. Kami tahu bahawa benang sedang melaksanakan beberapa tugas. Jika kita mahu tugas kita dapat dijalankan ( run), maka kita mesti menentukan benang untuk menjadi tertentu Runnable. Anda tidak boleh merosakkan Java dengan Thread: Bahagian VI - Ke penghalang!  - 1Untuk diingati, kita boleh 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();
}
Kami juga tahu bahawa kami mempunyai konsep seperti kunci. Kami membaca tentangnya dalam " Anda Tidak Boleh Merosakkan Java dengan Benang: Bahagian II - Penyegerakan ." Benang boleh menduduki kunci dan kemudian benang lain yang cuba mengisi kunci akan terpaksa menunggu kunci 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 rasa sudah tiba masanya untuk bercakap tentang perkara lain yang boleh kita lakukan yang menarik.

Semaphore

Cara paling mudah untuk mengawal bilangan utas yang boleh berfungsi secara serentak ialah semafor. Seperti di landasan kereta api. Lampu hijau menyala - anda boleh. Lampu merah menyala - kami sedang menunggu. Apa yang kita harapkan daripada semafor? kebenaran. Kebenaran dalam bahasa Inggeris - permit. Untuk mendapatkan kebenaran, anda perlu mendapatkannya, yang dalam bahasa Inggeris akan diperoleh. Dan apabila kebenaran itu tidak diperlukan lagi, kita mesti memberikannya, iaitu melepaskannya atau menyingkirkannya, yang dalam bahasa Inggeris akan dilepaskan. Mari lihat bagaimana ia berfungsi. Kami perlu mengimport kelas tersebut 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 kita dapat lihat, setelah menghafal perkataan Inggeris, kita memahami bagaimana semaphore berfungsi. Menariknya, syarat utama ialah "akaun" semaphore mesti mempunyai bilangan permit yang positif. Oleh itu, anda boleh memulakannya dengan tolak. Dan anda boleh meminta (memperoleh) lebih daripada 1.

CountDownLatch

Mekanisme seterusnya ialah CountDownLatch. Countdown dalam bahasa Inggeris ialah undur, dan Latch ialah bolt atau selak. Iaitu, jika kita menterjemahkannya, maka ini adalah selak dengan kira detik. Di sini kita memerlukan import kelas yang sesuai java.util.concurrent.CountDownLatch. Ia seperti perlumbaan atau perlumbaan di mana semua orang berkumpul di garisan permulaan dan apabila semua orang bersedia, kebenaran diberikan dan semua orang bermula pada masa 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();
 	}
}
tunggu dalam bahasa Inggeris - untuk mengharapkan. Maksudnya, kita bercakap dahulu countDown. Seperti yang dikatakan oleh Penterjemah Google, mengira detik ialah "tindakan mengira angka dalam susunan terbalik kepada sifar," iaitu, melakukan tindakan kira detik, yang tujuannya adalah untuk mengira hingga sifar. Dan kemudian kita katakan await- iaitu, tunggu sehingga nilai pembilang menjadi sifar. Adalah menarik bahawa kaunter sebegitu boleh guna. Seperti yang dikatakan dalam JavaDoc - "Apabila benang mesti berulang kali mengira mundur dengan cara ini, sebaliknya gunakan CyclicBarrier", iaitu, jika anda memerlukan pengiraan boleh guna semula, anda perlu menggunakan pilihan lain, yang dipanggil CyclicBarrier.

Penghalang Kitaran

Seperti namanya, CyclicBarrieria adalah penghalang kitaran. Kami perlu mengimport kelas tersebut java.util.concurrent.CyclicBarrier. Mari lihat 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, utas sedang melaksanakan await, iaitu menunggu. Dalam kes ini, nilai penghalang berkurangan. Penghalang dianggap pecah ( berrier.isBroken()) apabila kira detik mencapai sifar. Untuk menetapkan semula halangan, anda perlu memanggil berrier.reset(), yang tiada dalam CountDownLatch.

Penukar

Ubat seterusnya ialah Exchanger. Pertukaran daripada bahasa Inggeris diterjemahkan sebagai pertukaran atau pertukaran. A Exchangerialah penukar, iaitu sesuatu yang melaluinya mereka bertukar. Mari lihat contoh mudah:
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 melancarkan dua utas. Setiap daripada mereka melaksanakan kaedah pertukaran dan menunggu thread lain untuk turut melaksanakan kaedah pertukaran. Oleh itu, benang akan bertukar-tukar hujah yang diluluskan sesama mereka. Perkara yang menarik. Adakah dia tidak mengingatkan anda tentang apa-apa? Dan dia mengingatkan SynchronousQueue, yang terletak di tengah-tengah cachedThreadPool'a. Untuk kejelasan, berikut adalah contoh:
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 bahawa dengan melancarkan utas baharu, utas ini akan masuk ke mod menunggu, kerana baris gilir akan kosong. Dan kemudian mainbenang akan beratur teks "Mesej". Pada masa yang sama, ia akan berhenti untuk masa yang diperlukan sehingga ia menerima elemen teks ini daripada baris gilir. Mengenai topik ini anda juga boleh membaca " SynchronousQueue Vs Exchanger ".

Fasa

Dan akhirnya, perkara yang paling manis - Phaser. Kami perlu mengimport kelas tersebut java.util.concurrent.Phaser. Mari lihat contoh mudah:
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 bahawa halangan, apabila menggunakan Phaser'a, dipecahkan apabila bilangan pendaftaran bertepatan dengan bilangan ketibaan di penghalang. Anda boleh mengetahui lebih lanjut Phaserdalam artikel dari hab " Penyegerak Fasa Baharu ".

Keputusan

Seperti yang anda lihat daripada contoh, terdapat pelbagai cara untuk menyegerakkan benang. Tadi saya cuba mengingati sesuatu tentang multithreading, saya harap bahagian sebelum ini berguna. Mereka mengatakan bahawa laluan ke multithreading bermula dengan buku "Java Concurrency in Practice". Walaupun ia dikeluarkan pada tahun 2006, orang ramai menjawab bahawa buku itu agak asas dan masih mempunyai kelebihan. Sebagai contoh, anda boleh membaca perbincangan di sini: " Adakah Java Concurrency In Practice masih sah? ". Ia juga berguna untuk membaca pautan daripada perbincangan. Sebagai contoh, terdapat pautan ke buku " The Well-Grounded Java Developer ", di mana ia patut diberi perhatian kepada " Bab 4. Modern concurrency ". Terdapat satu lagi ulasan keseluruhan mengenai topik yang sama: " Adakah Java cocurrency in pracitce masih relevan dalam era java 8 ". Ia juga mempunyai petua tentang perkara lain yang perlu anda baca untuk benar-benar memahami topik tersebut. Selepas itu, anda boleh melihat dengan lebih dekat buku yang begitu menarik seperti " Ujian Amalan Pengaturcara OCA OCP JavaSE 8 ". Kami berminat dengan bahagian kedua iaitu OCP. Dan terdapat ujian dalam "∫". Buku ini mengandungi kedua-dua soalan dan jawapan dengan penjelasan. Sebagai contoh: Anda tidak boleh merosakkan Java dengan Thread: Bahagian VI - Ke penghalang!  - 3Ramai mungkin mula mengatakan bahawa ini hanyalah satu lagi hafalan kaedah. Di satu pihak, ya. Sebaliknya, soalan ini boleh dijawab dengan mengingati bahawa ExecutorServiceini adalah sejenis "naik taraf" Executor. Dan Executoria bertujuan semata-mata untuk menyembunyikan kaedah mencipta benang, tetapi bukan cara utama melaksanakannya, iaitu, berjalan dalam benang baru Runnable. Oleh itu, execute(Callable)tidak, kerana mereka hanya menambah kaedah ExecutorServiceyang boleh mengembalikan . Seperti yang anda lihat, kami boleh menghafal senarai kaedah, tetapi lebih mudah untuk meneka jika kami mengetahui sifat kelas itu sendiri. Nah, beberapa bahan tambahan mengenai topik: ExecutorsubmitFuture #Viacheslav
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION