CodeGym /Java Blogu /Rastgele /Birlikte daha iyi: Java ve Thread sınıfı. Bölüm VI - Ateş...
John Squirrels
Seviye
San Francisco

Birlikte daha iyi: Java ve Thread sınıfı. Bölüm VI - Ateş edin!

grupta yayınlandı

giriiş

İplikler ilginç bir şey. Geçmiş incelemelerde, çoklu iş parçacığını uygulamak için mevcut araçlardan bazılarına baktık. Başka ne ilginç şeyler yapabileceğimize bir bakalım. Bu noktada çok şey biliyoruz. Örneğin, " Birlikte daha iyi: Java ve Thread sınıfı. Bölüm I — Yürütme evreleri "nden, Thread sınıfının bir yürütme dizisini temsil ettiğini biliyoruz. Bir iş parçacığının bazı görevleri yerine getirdiğini biliyoruz. Eğer görevlerimizin yapabilmesini istiyorsak run, konuyu ile işaretlememiz gerekir Runnable. Hatırlamak için Tutorialspoint Online Java Compiler'ıBirlikte daha iyi: Java ve Thread sınıfı.  Bölüm VI - Ateş edin!  - 1 kullanabiliriz :

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();
}
Ayrıca kilit denen bir şeye sahip olduğumuzu da biliyoruz. Bunu " Birlikte daha iyi: Java ve Thread sınıfı. Kısım II — Senkronizasyon'da öğrendik . Bir iş parçacığı bir kilit alırsa, kilidi almaya çalışan başka bir iş parçacığı kilidin serbest bırakılmasını beklemeye zorlanır:

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();
	}
}
Sanırım yapabileceğimiz başka ilginç şeyler hakkında konuşmanın zamanı geldi.

Semaforlar

Aynı anda kaç iş parçacığının çalışabileceğini kontrol etmenin en basit yolu bir semafordur. Demiryolu sinyali gibi. Yeşil, devam anlamına gelir. Kırmızı, bekle demektir. Semafordan ne bekleyelim? Erişim. Erişmek için onu almalıyız. Erişime artık ihtiyaç kalmadığında, onu vermeli veya serbest bırakmalıyız. Bunun nasıl çalıştığını görelim. Sınıfı içe aktarmamız gerekiyor java.util.concurrent.Semaphore. Örnek:

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);
}
Gördüğünüz gibi, bu işlemler (alma ve bırakma) bir semaforun nasıl çalıştığını anlamamıza yardımcı olur. En önemli şey, eğer erişim elde edeceksek, semaforun pozitif sayıda izne sahip olması gerektiğidir. Bu sayı, negatif bir sayı olarak başlatılabilir. Ve 1'den fazla izin talebinde bulunabilir (alabiliriz).

Geri Sayım Mandalı

Bir sonraki mekanizma CountDownLatch. Şaşırtıcı olmayan bir şekilde, bu geri sayımlı bir mandal. Burada sınıf için uygun import ifadesine ihtiyacımız var java.util.concurrent.CountDownLatch. Herkesin başlangıç ​​çizgisinde toplandığı bir ayak yarışı gibi. Ve herkes hazır olduğunda, herkes aynı anda başlama sinyalini alır ve aynı anda başlar. Örnek:

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();
 	}
}
İlk önce mandala söyleyelim countDown(). Google, geri sayımı "sıfırdan geriye doğru sayma eylemi" olarak tanımlar. Ve sonra mandala şunu söyleriz await(), yani sayaç sıfır olana kadar bekleyin. İlginç bir şekilde, bu tek seferlik bir sayaçtır. Java belgeleri, "İpliklerin bu şekilde art arda geri sayması gerektiğinde, bunun yerine bir CyclicBarrier kullanın" diyor. Başka bir deyişle, yeniden kullanılabilir bir sayaca ihtiyacınız varsa, farklı bir seçeneğe ihtiyacınız vardır: CyclicBarrier.

Döngüsel Bariyer

Adından da anlaşılacağı gibi, CyclicBarrier"yeniden kullanılabilir" bir bariyerdir. Sınıfı içe aktarmamız gerekecek java.util.concurrent.CyclicBarrier. Bir örneğe bakalım:

public static void main(String[] args) throws InterruptedException {
	Runnable action = () -> System.out.println("On your mark!");
	CyclicBarrier barrier = new CyclicBarrier(3, action);
	Runnable task = () -> {
		try {
			barrier.await();
			System.out.println("Finished");
		} catch (BrokenBarrierException | InterruptedException e) {
			e.printStackTrace();
		}
	};
	System.out.println("Limit: " + barrier.getParties());
	for (int i = 0; i < 3; i++) {
		new Thread(task).start();
	}
}
Gördüğünüz gibi thread awaitmetodu çalıştırıyor yani bekliyor. Bu durumda bariyer değeri düşer. barrier.isBroken()Geri sayım sıfıra ulaştığında bariyer kırılmış ( ) kabul edilir . Bariyeri sıfırlamak için, sahip olmayan reset()yöntemi çağırmanız gerekir.CountDownLatch

eşanjör

Bir sonraki mekanizma Eşanjördür. Bu bağlamda, bir Değişim, bir şeylerin değiş tokuş edildiği veya değiştirildiği bir senkronizasyon noktasıdır. Beklediğiniz gibi an, Exchangertakas veya takas gerçekleştiren bir sınıftır. En basit örneğe bakalım:

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() + " exchanged with " + withThreadName);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	};
	new Thread(task).start();
	new Thread(task).start();
}
Burada iki iş parçacığı başlatıyoruz. Her biri değişim yöntemini çalıştırır ve diğer iş parçacığının da değişim yöntemini çalıştırmasını bekler. Bunu yaparken, ileti dizileri iletilen bağımsız değişkenleri değiş tokuş eder. İlginç. Size bir şey hatırlatmıyor mu? SynchronousQueueKalbinde yatan 'ı anımsatıyor CachedThreadPool. Netlik için, işte bir örnek:

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");
}
Örnek, yeni bir iş parçacığı başlatıldığında sıra boş olacağından bekleyeceğini göstermektedir. Ve sonra ana iş parçacığı "Mesaj" dizesini kuyruğa koyar. Ayrıca, bu dizi sıradan alınana kadar duracaktır. Bu konu hakkında daha fazla bilgi edinmek için " SynchronousQueue ve Exchanger " makalesini de okuyabilirsiniz .

fazer

En iyisini sona sakladık — Phaser. Sınıfı içe aktarmamız gerekecek java.util.concurrent.Phaser. Basit bir örneğe bakalım:

public static void main(String[] args) throws InterruptedException {
        Phaser phaser = new Phaser();
        // By calling the register method, we register the current (main) thread as a party
        phaser.register();
        System.out.println("Phasecount is " + phaser.getPhase());
        testPhaser(phaser);
        testPhaser(phaser);
        testPhaser(phaser);
        // After 3 seconds, we arrive at the barrier and deregister. Number of arrivals = number of registrations = start
        Thread.sleep(3000);
        phaser.arriveAndDeregister();
        System.out.println("Phasecount is " + phaser.getPhase());
    }

    private static void testPhaser(final Phaser phaser) {
        // We indicate that there will be a +1 party on the Phaser
        phaser.register();
        // Start a new thread
        new Thread(() -> {
            String name = Thread.currentThread().getName();
            System.out.println(name + " arrived");
            phaser.arriveAndAwaitAdvance(); // The threads register arrival at the phaser.
            System.out.println(name + " after passing barrier");
        }).start();
    }
Örnek, kullanılırken Phaser, kayıt sayısı engele gelenlerin sayısıyla eşleştiğinde engelin kırıldığını göstermektedir. Bu GeeksforGeeks makalesiniPhaser okuyarak daha fazla aşina olabilirsiniz .

Özet

Bu örneklerden de görebileceğiniz gibi, dizileri senkronize etmenin çeşitli yolları vardır. Daha önce, çoklu okumanın özelliklerini hatırlamaya çalıştım. Umarım bu serideki önceki taksitler faydalı olmuştur. Bazı insanlar çoklu iş parçacığına giden yolun "Uygulamada Java Eş Zamanlılığı" kitabıyla başladığını söylüyor. 2006 yılında piyasaya sürülmesine rağmen, insanlar kitabın oldukça temel olduğunu ve bugün hala geçerli olduğunu söylüyor. Örneğin, tartışmayı buradan okuyabilirsiniz: "Uygulamada Java Eş Zamanlılığı" hala geçerli mi? . Tartışmadaki bağlantıları okumak da yararlıdır. Örneğin, The Well-Grounded Java Developer kitabına bir bağlantı var ve Bölüm 4'ten özellikle bahsedeceğiz. Modern eşzamanlılık . Bu konuyla ilgili tam bir inceleme de var:"Uygulamada Java Eş Zamanlılığı" Java 8 Çağında Hala Geçerli mi? Bu makale ayrıca, bu konuyu gerçekten anlamak için başka ne okumanız gerektiğine dair öneriler sunar. Ardından OCA/OCP Java SE 8 Programmer Practice Tests gibi harika bir kitaba göz atabilirsiniz . İkinci kısaltmayla ilgileniyoruz: OCP (Oracle Certified Professional). Testleri "Bölüm 20: Java Eş Zamanlılığı"nda bulacaksınız. Bu kitapta hem sorular hem de açıklamalarla birlikte cevaplar var. Örneğin: Birlikte daha iyi: Java ve Thread sınıfı.  Bölüm VI - Ateş edin!  - 3Pek çok kişi bu sorunun yöntemlerin ezberlenmesine başka bir örnek olduğunu söylemeye başlayabilir. Bir yandan, evet. ExecutorServiceÖte yandan, bu soruyu bunun bir tür "yükseltme" olduğunu hatırlayarak yanıtlayabilirsiniz Executor. VeExecutorRunnablesadece iş parçacıklarının oluşturulma şeklini gizlemeyi amaçlar, ancak bunları yürütmenin, yani yeni bir iş parçacığında bir nesne başlatmanın ana yolu değildir . Bu yüzden yoktur execute(Callable)- çünkü içinde ExecutorService, Executorbasitçe submit()bir nesneyi döndürebilecek yöntemler ekler Future. Tabii ki, bir yöntem listesi ezberleyebiliriz, ancak sınıfların doğası hakkındaki bilgimize dayanarak yanıtımızı vermek çok daha kolaydır. Ve işte konuyla ilgili bazı ek materyaller: Birlikte daha iyi: Java ve Thread sınıfı. Bölüm I — Yürütme konuları Birlikte daha iyi: Java ve Thread sınıfı. Bölüm II — Eşitleme Birlikte daha iyi: Java ve Thread sınıfı. Bölüm III — Etkileşim Birlikte Daha İyi: Java ve Thread sınıfı. Bölüm IV — Callable, Future ve arkadaşlar Birlikte daha iyi: Java ve Thread sınıfı. Bölüm V — Yürütücü, ThreadPool, Fork/Join
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION