JavaRush /Java блогу /Random-KY /Сиз Javaны жип менен буза албайсыз: VI бөлүк - Тоскоолдук...
Viacheslav
Деңгээл

Сиз Javaны жип менен буза албайсыз: VI бөлүк - Тоскоолдукка!

Группада жарыяланган

Киришүү

Агымдар кызыктуу нерсе. Мурунку серептерде биз көп агымды ишке ашыруу үчүн жеткorктүү куралдардын айрымдарын карап чыктык. Келгиле, дагы кандай кызыктуу нерселерди кыла аларыбызды карап көрөлү. Бул учурда биз көп нерсени билебиз. Мисалы, " Жаваны жип менен буза албайсыз: I бөлүм - Жиптер " дегенден биз жип жип экенин билебиз. Жип кандайдыр бир тапшырманы аткарып жатканын билебиз. Эгерде биз тапшырмабыз иштей алышын кааласак ( run), анда биз жипти белгилүү болушу үчүн көрсөтүшүбүз керек Runnable. Эсиңизде болсун, биз Tutorialspoint Java Online CompilerСиз Javaны жип менен буза албайсыз: VI бөлүк - Тоскоолдукка!  - 1 колдоно алабыз :
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();
}
Бизде кулпу деген түшүнүк бар экенин да билебиз. Биз бул тууралуу " Сиз Java-ны жип менен буза албайсыз: II бөлүк - Синхрондоштуруу ." Жип кулпуну ээлей алат, андан кийин кулпуну ээлөөгө аракет кылган башка жип кулпунун бош болушун күтүүгө аргасыз болот:
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();
	}
}
Мен кызыктуу дагы эмне кыла аларыбыз жөнүндө сүйлөшүүгө убакыт келди деп ойлойм.

Семафорлор

Канча жип бир убакта иштей аларын көзөмөлдөөнүн эң жөнөкөй каражаты - семафор. Темир жолдогудай. Жашыл жарык күйүп турат - мүмкүн. Кызыл жарык күйүп турат - биз күтүп жатабыз. Семафордон эмнени күтөбүз? Уруксаттар. Англис тorнде уруксат - уруксат. Уруксат алуу үчүн, аны алуу керек, ал англис тorнде алынат. Ал эми уруксат керек болбой калганда, биз аны беришибиз керек, башкача айтканда, аны бошотушубуз же андан кутулуубуз керек, ал англисче чыгарылат. Келгиле, анын кантип иштээрин карап көрөлү. Биз классты импорттообуз керек болот 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. Англис тorндеги Countdown - бул артка саноо, ал эми Латч - болт же кулпу. Башкача айтканда, эгер биз аны которсок, анда бул артка санауу менен бекитүү. Бул жерде бизге класстын тиешелүү импорту керек java.util.concurrent.CountDownLatch. Бул жарыш же жарыш сыяктуу, баары старт сызыгына чогулуп, баары даяр болгондо уруксат берorп, баары бир убакта башталат. Мисал:
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. Google Translator айткандай, артка санауу "нөлгө тескери тартипте сандарды саноо актысы" болуп саналат, башкача айтканда, нөлгө чейин эсептөө болгон артка санак аракетин аткаруу. Анан биз айтабыз 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. Англис тorнен алмашуу алмашуу же алмашуу деп которулат. А 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'ды колдонууда, каттоолордун саны тоскоолдукка келгендердин санына дал келгенде, тоскоолдук бузулат. Көбүрөөк маалыматты " New Phaser synchronizerPhaser " хабынан макаладан биле аласыз .

Жыйынтыктар

Мисалдардан көрүнүп тургандай, жиптерди синхрондоштуруунун ар кандай жолдору бар. Мурда мен multithreading жөнүндө бир нерсени эстегенге аракет кылдым, мурунку бөлүктөрү пайдалуу болду деп үмүттөнөм. Алар көп агымдын жолу "Практикадагы Java конкуренциясы" китебинен башталат дешет. Ал 2006-жылы чыкканына карабастан, адамдар бул китеп абдан фундаменталдуу жана дагы эле бир муштум деп жооп беришет. Мисалы, бул жерден талкууларды окуй аласыз: " Java Concurrency In Practice дагы эле жарактуубу? ". Талкуудагы шилтемелерди окуу да пайдалуу. Мисалы, " Жакшы негизделген Java иштеп чыгуучу " китебине шилтеме бар , анда " 4-бөлүм. Заманбап параллелдүүлүк " дегенге көңүл буруу керек . Ушул эле тема боюнча дагы бир бүтүн кароо бар: “ Java 8дин доорунда Java коcurrency иш жүзүндө дагы эле актуалдуубу ”. Ошондой эле теманы чындап түшүнүү үчүн дагы эмнени окуу керектиги боюнча кеңештер бар. Андан кийин, сиз " OCA OCP JavaSE 8 программист практикалык тесттери " сыяктуу сонун китепти жакындан көрө аласыз . Бизди экинчи бөлүк, башкача айтканда, OCP кызыктырат. Ал эми "∫" ичинде тесттер бар. Бул китепте суроолор да, жооптор да бар. Мисалы: Сиз Javaны жип менен буза албайсыз: VI бөлүк - Тоскоолдукка!  - 3Көптөр бул ыкмалардын кезектеги жаттоосу деп айта башташы мүмкүн. Бир жагынан, ооба. Башка жагынан алып караганда, бул суроого ExecutorServiceбул "жаңыртуу" түрү экенин эстеп жооп берсе болот Executor. Ал Executorжөн гана жиптерди түзүү ыкмасын жашыруу үчүн арналган, бирок аларды аткаруунун негизги жолу эмес, башкача айтканда, жаңы жипте иштөө Runnable. Ошондуктан, execute(Callable)жок, анткени алар жөн гана кайтып келе турган ExecutorServiceыкмаларды Executorкошушту . Көрүнүп тургандай, биз ыкмалардын тизмесин жаттай алабыз, бирок класстардын мүнөзүн билсек, болжолдоо алда канча оңой. Ооба, тема боюнча кээ бир кошумча материалдар: submitFuture #Вячеслав
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION