JavaRush /وبلاگ جاوا /Random-FA /شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت ششم - ...
Viacheslav
مرحله

شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت ششم - به مانع!

در گروه منتشر شد

معرفی

جریان چیز جالبی است. در بررسی های قبلی، ما به برخی از ابزارهای موجود برای اجرای multithreading نگاه کردیم. بیایید ببینیم چه کارهای جالب دیگری می توانیم انجام دهیم. در این مرحله ما چیزهای زیادی می دانیم. به عنوان مثال، از " شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت اول - موضوعات "، ما می دانیم که یک موضوع یک موضوع است. ما می دانیم که یک رشته در حال انجام برخی وظایف است. اگر می‌خواهیم وظیفه ما قابل اجرا باشد ( run)، باید موضوع را مشخص کنیم Runnable. شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت ششم - به مانع!  - 1برای یادآوری، می‌توانیم از 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();
}
ما همچنین می دانیم که مفهومی به عنوان قفل داریم. ما در مورد آن در " شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت دوم - همگام سازی " خواندیم . یک نخ می تواند یک قفل را اشغال کند و سپس رشته دیگری که سعی می کند قفل را اشغال کند مجبور می شود منتظر بماند تا قفل آزاد شود:
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();
	}
}
فکر می کنم وقت آن رسیده است که در مورد اینکه چه کارهای دیگری می توانیم انجام دهیم که جالب است صحبت کنیم.

سمافورها

ساده ترین وسیله برای کنترل اینکه چند رشته می توانند به طور همزمان کار کنند، سمافور است. مثل راه آهن چراغ سبز روشن است - شما می توانید. چراغ قرمز روشن است - ما منتظریم. ما از یک سمافور چه انتظاری داریم؟ مجوزها مجوز به زبان انگلیسی - مجوز. برای دریافت مجوز، باید آن را دریافت کنید، که در زبان انگلیسی به عنوان خرید خواهد بود. و وقتی دیگر نیازی به اجازه نیست، باید آن را بدهیم، یعنی آن را رها کنیم یا خلاص کنیم، که در انگلیسی آزاد می شود. بیایید ببینیم چگونه کار می کند. ما باید کلاس را وارد کنیم java.util.concurrent.Semaphore. مثال:
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);
}
همانطور که می بینیم، با حفظ کردن کلمات انگلیسی، متوجه می شویم که سمافور چگونه کار می کند. جالب است که شرط اصلی این است که "حساب" سمافور باید دارای تعداد مجوز مثبت باشد. بنابراین، می توانید آن را با منهای شروع کنید. و شما می توانید بیش از 1 درخواست (کسب) کنید.

CountDown Latch

مکانیسم بعدی این است CountDownLatch. شمارش معکوس در انگلیسی یک شمارش معکوس است و Latch یک پیچ یا چفت است. یعنی اگر آن را ترجمه کنیم، این یک چفت با شمارش معکوس است. در اینجا به ایمپورت مناسب کلاس نیاز داریم java.util.concurrent.CountDownLatch. مثل یک مسابقه یا مسابقه ای است که همه در خط شروع جمع می شوند و وقتی همه آماده هستند، اجازه داده می شود و همه همزمان شروع می کنند. مثال:
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();
 	}
}
انتظار در انگلیسی - انتظار. یعنی اول حرف می زنیم countDown. همانطور که مترجم گوگل می گوید، شمارش معکوس "عملی برای شمارش اعداد به ترتیب معکوس تا صفر" است، یعنی انجام یک عمل شمارش معکوس، که هدف آن شمارش تا صفر است. و سپس می گوییم await- یعنی صبر کنید تا مقدار شمارنده صفر شود. جالب است که چنین پیشخوانی یکبار مصرف است. همانطور که در JavaDoc گفته شده است - "وقتی رشته ها باید به طور مکرر به این ترتیب شمارش معکوس کنند، به جای آن از CyclicBarrier استفاده کنید"، یعنی اگر به شمارش قابل استفاده مجدد نیاز دارید، باید از گزینه دیگری استفاده کنید که به نام CyclicBarrier.

Cyclic Barrier

همانطور که از نام آن پیداست، CyclicBarrierیک مانع چرخه ای است. ما باید کلاس را وارد کنیم java.util.concurrent.CyclicBarrier. بیایید به یک مثال نگاه کنیم:
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();
	}
}
همانطور که می بینید، موضوع در حال اجرا است await، یعنی منتظر است. در این حالت، مقدار مانع کاهش می یابد. berrier.isBroken()وقتی شمارش معکوس به صفر برسد، مانع شکسته ( ) در نظر گرفته می شود . برای بازنشانی مانع، باید تماس بگیرید berrier.reset()، که در آن وجود نداشت CountDownLatch.

مبدل

راه حل بعدی این است Exchanger. مبادله از انگلیسی به عنوان مبادله یا مبادله ترجمه می شود. الف Exchangerمبدل است، یعنی چیزی که از طریق آن مبادله می کنند. بیایید به یک مثال ساده نگاه کنیم:
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();
}
در اینجا ما دو موضوع را راه اندازی می کنیم. هر کدام از آنها متد Exchange را اجرا می‌کنند و منتظر می‌مانند تا thread دیگری نیز متد Exchange را اجرا کند. بنابراین، تاپیک ها آرگومان های تصویب شده را بین خود رد و بدل خواهند کرد. نکته جالب. او چیزی را به شما یادآوری نمی کند؟ و یادآوری می کند SynchronousQueueکه در قلب cachedThreadPool«ع» نهفته است. برای وضوح، در اینجا یک مثال است:
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");
}
مثال نشان می دهد که با راه اندازی یک موضوع جدید، این موضوع به حالت انتظار می رود، زیرا صف خالی خواهد بود و سپس mainموضوع متن "پیام" را در صف قرار می دهد. در همان زمان، تا زمانی که این عنصر متن را از صف دریافت کند، برای مدت زمان مورد نیاز متوقف می شود. در مورد این موضوع همچنین می توانید " SynchronousQueue Vs Exchanger " را بخوانید.

فازر

و در نهایت، شیرین ترین چیز - Phaser. ما باید کلاس را وارد کنیم java.util.concurrent.Phaser. بیایید به یک مثال ساده نگاه کنیم:
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();
    }
مثال نشان می‌دهد که هنگام استفاده از Phaser«a»، مانع شکسته می‌شود که تعداد ثبت‌نام‌ها با تعداد ورودی‌ها به مانع منطبق شود. می توانید Phaserدر مقاله از هاب " همگام ساز جدید فازر " اطلاعات بیشتری کسب کنید.

نتایج

همانطور که از مثال ها می بینید، روش های مختلفی برای همگام سازی نخ ها وجود دارد. قبلاً سعی کردم چیزی در مورد Multithreading به خاطر بسپارم، امیدوارم قسمت های قبلی مفید بوده باشند. آنها می گویند مسیر چند رشته ای با کتاب "همزمانی جاوا در عمل" آغاز می شود. اگرچه این کتاب در سال 2006 منتشر شد، مردم پاسخ می‌دهند که این کتاب کاملاً اساسی است و هنوز هم تاثیرگذار است. به عنوان مثال، می توانید بحث های اینجا را بخوانید: " آیا همزمانی جاوا در عمل هنوز معتبر است؟ ". خواندن پیوندهای بحث نیز مفید است. به عنوان مثال، پیوندی به کتاب " توسعه‌دهنده جاوای خوب " وجود دارد که در آن ارزش توجه به " فصل 4. همزمانی مدرن " را دارد. بررسی کامل دیگری در مورد همین موضوع وجود دارد: " آیا هم ارزی جاوا در عمل هنوز در عصر جاوا 8 مرتبط است ". همچنین نکاتی در مورد موارد دیگری که باید بخوانید تا واقعاً موضوع را درک کنید، دارد. پس از آن، می توانید به کتاب فوق العاده ای مانند " تست های تمرین برنامه نویس OCA OCP JavaSE 8 " نگاه دقیق تری بیندازید. ما به قسمت دوم یعنی OCP علاقه مندیم. و تست هایی در "🔻" وجود دارد. این کتاب شامل پرسش و پاسخ همراه با توضیحات است. به عنوان مثال: شما نمی توانید جاوا را با یک موضوع خراب کنید: قسمت ششم - به مانع!  - 3بسیاری ممکن است شروع کنند به این که این فقط یکی دیگر از حفظ روش ها است. از یک طرف بله. از سوی دیگر، این سوال را می توان با یادآوری اینکه ExecutorServiceاین یک نوع "ارتقا" است پاسخ داد Executor. و Executorصرفاً برای پنهان کردن روش ایجاد رشته ها در نظر گرفته شده است، اما نه روش اصلی اجرای آنها، یعنی اجرای در یک رشته جدید Runnable. بنابراین، execute(Callable)نه، زیرا آنها به سادگی روش هایی را اضافه کردند ExecutorServiceکه می توانند برگردند . همانطور که می بینید، ما می توانیم لیستی از روش ها را به خاطر بسپاریم، اما اگر ماهیت خود کلاس ها را بدانیم، حدس زدن آن بسیار آسان تر است. خوب، برخی از مطالب اضافی در مورد موضوع: ExecutorsubmitFuture #ویاچسلاو
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION