JavaRush /مدونة جافا /Random-AR /لا يمكنك إفساد جافا بموضوع: الجزء السادس - إلى الحاجز!
Viacheslav
مستوى

لا يمكنك إفساد جافا بموضوع: الجزء السادس - إلى الحاجز!

نشرت في المجموعة

مقدمة

التدفقات شيء مثير للاهتمام. في المراجعات السابقة، نظرنا إلى بعض الأدوات المتاحة لتنفيذ تعدد العمليات. دعونا نرى ما الأشياء الأخرى المثيرة للاهتمام التي يمكننا القيام بها. في هذه المرحلة نحن نعرف الكثير. على سبيل المثال، من " لا يمكنك إفساد جافا بخيط: الجزء الأول - الخيوط "، نعلم أن سلسلة الرسائل هي سلسلة رسائل. نحن نعلم أن الخيط يؤدي بعض المهام. إذا أردنا أن تكون مهمتنا قادرة على التشغيل ( 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.

CountDownLatch

الآلية التالية هي CountDownLatch. العد التنازلي في اللغة الإنجليزية هو العد التنازلي، والمزلاج هو الترباس أو المزلاج. وهذا هو، إذا قمنا بترجمته، فهذا مزلاج مع العد التنازلي. نحن هنا بحاجة إلى الاستيراد المناسب للفئة 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.

CyclicBarrier

كما يوحي الاسم، 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();
}
هنا نطلق موضوعين. ينفذ كل واحد منهم طريقة التبادل وينتظر مؤشر ترابط آخر لتنفيذ طريقة التبادل أيضًا. وبالتالي، فإن المواضيع سوف تتبادل الحجج التي تم تمريرها فيما بينها. شيء مثير للاهتمام. ألا تذكرك بأي شيء؟ ويذكر 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في المقالة من المحور " New Phaser Synchronizer ".

نتائج

كما ترون من الأمثلة، هناك طرق مختلفة لمزامنة المواضيع. حاولت سابقًا أن أتذكر شيئًا عن تعدد العمليات، وآمل أن تكون الأجزاء السابقة مفيدة. يقولون أن الطريق إلى تعدد العمليات يبدأ بكتاب "Java Concurrency in Practice". على الرغم من أنه صدر في عام 2006، إلا أن الناس يجيبون بأن الكتاب أساسي جدًا ولا يزال يحمل الكثير من النجاح. على سبيل المثال، يمكنك قراءة المناقشات هنا: " هل لا تزال Java Concurrency In Practice صالحة؟ ". من المفيد أيضًا قراءة الروابط من المناقشة. على سبيل المثال، يوجد رابط لكتاب " The Well-Grounded Java Developer "، حيث يجدر الانتباه إلى " الفصل 4. التزامن الحديث ". هناك مراجعة كاملة أخرى حول نفس الموضوع: " هل لا تزال عملة Java المشتركة في الممارسة العملية ذات صلة في عصر Java 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