Java for Beginner
672 subscribers
541 photos
155 videos
12 files
827 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Мы научим Вас производить впечатление на HR😂😎

https://t.me/Java_for_beginner_dev

#Mems
Напоминаю!

Сегодня в 16:00 по МСК, мы вновь соберемся онлайн и продолжим дописывать наше консольное TODO-приложение.

Приходите, будет интересно)😉
This media is not supported in your browser
VIEW IN TELEGRAM
Запись нашей сегодняшней встречи -
https://www.youtube.com/watch?v=qNwDkOGJ96Q

Спасибо всем кто участвовал👍, продолжение на следующей неделе)))
Асинхронизм и Future в Java

Асинхронное программирование позволяет выполнять задачи в фоновом режиме, освобождая основной поток для выполнения других операций. Это особенно полезно для задач ввода-вывода, длительных вычислений и работы с внешними сервисами.

Асинхронное выполнение с использованием Future

Интерфейс Future из пакета java.util.concurrent представляет собой результат асинхронной операции. С помощью Future можно проверить, завершилась ли задача, дождаться ее завершения и получить результат.

Пример использования Future и ExecutorService:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureExample {

public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();

Callable<Integer> task = () -> {
Thread.sleep(2000);
return 42;
};

Future<Integer> future = executorService.submit(task);

// Выполняем другие задачи
System.out.println("Doing something else while the task is running...");

try {
// Блокируемся до завершения задачи и получаем результат
Integer result = future.get();
System.out.println("Task completed with result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
}


Методы интерфейса Future

boolean cancel(boolean mayInterruptIfRunning): Пытается отменить выполнение задачи.
boolean isCancelled(): Возвращает true, если задача была отменена.
boolean isDone(): Возвращает true, если задача завершена.
V get(): Блокирует до завершения задачи и возвращает результат.
V get(long timeout, TimeUnit unit): Блокирует до завершения задачи или до истечения тайм-аута.


Пример использования методов Future:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class FutureMethodsExample {

public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();

Callable<Integer> task = () -> {
Thread.sleep(3000);
return 42;
};

Future<Integer> future = executorService.submit(task);

try {
// Проверяем, завершена ли задача
if (!future.isDone()) {
System.out.println("Task is not completed yet...");
}

// Пытаемся получить результат с тайм-аутом
Integer result = future.get(2, TimeUnit.SECONDS);
System.out.println("Task completed with result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} catch (java.util.concurrent.TimeoutException e) {
System.out.println("Task timed out.");
} finally {
future.cancel(true); // Отменяем задачу, если она еще не завершена
executorService.shutdown();
}
}
}


#Java #Training #Multithreading #Medium
Что выведет код?

public class ArrayExample {
public static void main(String[] args) {
int[] array = new int[]{1, 2, 3, 4, 5};
int sum = 0;

for (int i = 0; i < array.length; i++) {
if (i % 2 == 0) {
sum += array[i];
} else {
sum -= array[i];
}
}

System.out.println(sum);
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
10%
4
0%
5
10%
-2
80%
3
Мы все когда-то начинали с Basic😂

https://t.me/Java_for_beginner_dev

#Mems
CompletableFuture в Java

CompletableFuture — это расширение Future, которое предоставляет мощные средства для работы с асинхронными задачами. Он позволяет легко комбинировать и связывать асинхронные операции, использовать методы обратного вызова (callback) и работать с исключениями.

Создание и выполнение задач с помощью CompletableFuture
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureExample {

public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 42;
});

System.out.println("Doing something else while the task is running...");

try {
Integer result = future.get();
System.out.println("Task completed with result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}


Методы thenApply, thenAccept и thenRun

thenApply(Function): Применяет функцию к результату завершенного CompletableFuture и возвращает новый CompletableFuture.
thenAccept(Consumer): Принимает потребителя для обработки результата завершенного CompletableFuture.
thenRun(Runnable): Выполняет действие после завершения CompletableFuture, не используя его результат.


Пример использования:
import java.util.concurrent.CompletableFuture;

public class CompletableFutureChainingExample {

public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 42;
}).thenApply(result -> result * 2)
.thenAccept(result -> System.out.println("Result: " + result))
.thenRun(() -> System.out.println("Task completed!"));
}
}


Обработка исключений

CompletableFuture предоставляет методы для обработки исключений, такие как exceptionally, handle и whenComplete.

Пример обработки исключений:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureExceptionHandlingExample {

public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (true) {
throw new RuntimeException("Something went wrong!");
}
return 42;
}).exceptionally(ex -> {
System.out.println("Exception: " + ex.getMessage());
return -1;
});

try {
Integer result = future.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}

#Java #Training #Multithreading #Medium
Комбинирование CompletableFuture

Методы thenCombine и thenCompose позволяют комбинировать несколько CompletableFuture.

Пример использования thenCombine:

import java.util.concurrent.CompletableFuture;

public class CompletableFutureCombineExample {

public static void main(String[] args) {
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 42;
});

CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 24;
});

CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, (result1, result2) -> result1 + result2);

try {
System.out.println("Combined result: " + combinedFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}


Пример использования thenCompose:
import java.util.concurrent.CompletableFuture;

public class CompletableFutureComposeExample {

public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 42;
});

CompletableFuture<Integer> composedFuture = future.thenCompose(result ->
CompletableFuture.supplyAsync(() -> result * 2)
);

try {
System.out.println("Composed result: " + composedFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}


#Java #Training #Multithreading #Medium
Многопоточность в Java: volatile и Immutable Classes

Volatile

Ключевое слово volatile в Java используется для обозначения переменных, которые могут быть изменены различными потоками. Переменная, объявленная как volatile, гарантирует, что все потоки будут читать ее актуальное значение из основной памяти, а не из кэша процессора. Это помогает избежать некоторых проблем с видимостью, которые могут возникнуть в многопоточных приложениях.

Пример использования volatile
public class VolatileExample {
private volatile boolean running = true;

public void start() {
new Thread(() -> {
while (running) {
System.out.println("Thread is running...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Thread stopped.");
}).start();
}

public void stop() {
running = false;
}

public static void main(String[] args) {
VolatileExample example = new VolatileExample();
example.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
example.stop();
}
}

В этом примере поток проверяет значение переменной running и останавливается, когда она становится false. Использование volatile гарантирует, что изменения переменной running будут видны всем потокам.



Immutable Classes

Иммутабельные (неизменяемые) классы — это классы, состояния объектов которых не могут быть изменены после создания. Они особенно полезны в многопоточной среде, так как обеспечивают безопасность потоков (thread-safety) без необходимости использования синхронизации.

Пример неизменяемого класса
public final class ImmutablePerson {
private final String name;
private final int age;

public ImmutablePerson(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}

@Override
public String toString() {
return "ImmutablePerson{name='" + name + "', age=" + age + "}";
}

public static void main(String[] args) {
ImmutablePerson person = new ImmutablePerson("John", 30);
System.out.println(person);
// person.setName("Doe"); // This would cause a compile error
}
}


В этом примере класс ImmutablePerson является неизменяемым, потому что все его поля final, и они инициализируются только один раз в конструкторе.

Преимущества неизменяемых классов

Потокобезопасность: Нет необходимости в синхронизации, так как состояние объекта не может быть изменено после создания.
Простота использования: Меньше ошибок, связанных с изменением состояния.
Упрощенная разработка: Легче проектировать и отлаживать.


#Java #Training #Multithreading #Medium #Volatile #Immutable_Classes
Что выведет код?

public class VariableChallenge {
public static void main(String[] args) {
int x = 5;
final int y;

if (x > 3) {
y = 10;
} else {
y = 20;
}

x = x + y;

int z = x > 15 ? x * 2 : x / 2;

System.out.println(z);
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
10%
30
0%
25
20%
15
70%
7
Долбанные разработчики 😂

https://t.me/Java_for_beginner_dev

#Mems
Многопоточность в Java: Deadlock

Deadlock (взаимная блокировка) — это состояние, при котором два или более потоков блокируют друг друга, ожидая освобождения ресурсов, занятых друг другом. Это приводит к тому, что ни один из потоков не может продолжать выполнение.
public class DeadlockExample {

private final Object lock1 = new Object();
private final Object lock2 = new Object();

public void method1() {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}

public void method2() {
synchronized (lock2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) {
System.out.println("Thread 2: Holding lock 2 & 1...");
}
}
}

public static void main(String[] args) {
DeadlockExample example = new DeadlockExample();

Thread thread1 = new Thread(example::method1);
Thread thread2 = new Thread(example::method2);

thread1.start();
thread2.start();
}
}

В этом примере возникает deadlock, потому что thread1 захватывает lock1 и ждет lock2, в то время как thread2 захватывает lock2 и ждет lock1. Таким образом, оба потока оказываются в состоянии взаимной блокировки.

Способы предотвращения Deadlock

Иерархия блокировок: Всегда захватывайте ресурсы в определенном порядке.
Тайм-ауты: Используйте методы с тайм-аутами для захвата блокировок, такие как tryLock из ReentrantLock.
Избегайте вложенных блокировок: Старайтесь минимизировать количество вложенных блокировок.


Пример с использованием tryLock
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;

public class DeadlockAvoidanceExample {

private final Lock lock1 = new ReentrantLock();
private final Lock lock2 = new ReentrantLock();

public void method1() {
try {
if (lock1.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 1: Holding lock 1...");
Thread.sleep(50);
if (lock2.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 1: Holding lock 1 & 2...");
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void method2() {
try {
if (lock2.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 2: Holding lock 2...");
Thread.sleep(50);
if (lock1.tryLock(50, TimeUnit.MILLISECONDS)) {
try {
System.out.println("Thread 2: Holding lock 2 & 1...");
} finally {
lock1.unlock();
}
}
} finally {
lock2.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
DeadlockAvoidanceExample example = new DeadlockAvoidanceExample();

Thread thread1 = new Thread(example::method1);
Thread thread2 = new Thread(example::method2);

thread1.start();
thread2.start();
}
}

В этом примере tryLock используется для попытки захвата блокировок с тайм-аутом, что предотвращает возникновение deadlock.

#Java #Training #Multithreading #Medium #Deadlock
Многопоточность в Java: Пул потоков

Пул потоков (Thread Pool) — это механизм управления коллекцией потоков, предназначенный для выполнения задач. Потоки в пуле повторно используются для выполнения нескольких задач, что позволяет сократить накладные расходы на создание и уничтожение потоков и повысить производительность многопоточных приложений.

Зачем нужен пул потоков?

Улучшение производительности: Создание нового потока — дорогостоящая операция. Пул потоков позволяет избежать этих накладных расходов, повторно используя уже существующие потоки.
Контроль над количеством потоков: Пул потоков позволяет ограничить максимальное количество одновременно работающих потоков, что предотвращает исчерпание ресурсов системы.
Упрощение управления задачами: Пул потоков предоставляет удобные методы для отправки задач на выполнение, что упрощает управление многопоточными задачами.


Создание пула потоков

В Java для работы с пулом потоков используется класс ExecutorService, который предоставляет методы для управления потоками и выполнения задач. Воспользоваться готовыми реализациями можно с помощью класса Executors.

Пример создания пула потоков с фиксированным числом потоков
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolExample {

public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);

for (int i = 0; i < 5; i++) {
int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is running in thread " + Thread.currentThread().getName());
try {
Thread.sleep(2000); // Симуляция долгой задачи
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " completed");
});
}

executorService.shutdown();
}
}


В этом примере создается пул из трех потоков, и пять задач отправляются на выполнение. Задачи будут выполняться параллельно, но не более трех одновременно.



Виды пулов потоков

FixedThreadPool: Пул с фиксированным числом потоков. Новый поток не создается, если все потоки заняты, до тех пор, пока один из них не освободится.
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);


CachedThreadPool: Пул, который создает потоки по мере необходимости, но переиспользует старые потоки, если они доступны. Подходит для выполнения большого количества коротких задач.
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();


SingleThreadExecutor: Пул с единственным потоком, выполняющий задачи последовательно.
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();


ScheduledThreadPool: Пул, который позволяет выполнять задачи с задержкой или периодически.
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);


#Java #Training #Multithreading #Medium
Варианты ответа:
Anonymous Quiz
80%
3
0%
4
0%
5
20%
7
Что выведет код?

public class StringChallenge {
public static void main(String[] args) {
String str1 = "Java";
String str2 = new String("Java");
String str3 = "Ja" + "va";
String str4 = str2.intern();

boolean result1 = (str1 == str2);
boolean result2 = (str1 == str3);
boolean result3 = (str1 == str4);
boolean result4 = str1.equals(str2);

int sum = (result1 ? 1 : 0) + (result2 ? 1 : 0) + (result3 ? 1 : 0) + (result4 ? 1 : 0);

System.out.println(sum);
}
}


#Tasks
По-любому у каждого так😉

https://t.me/Java_for_beginner_dev

#Mems
Многопоточность в Java: ExecutorService

ExecutorService — это интерфейс, который предоставляет методы для управления жизненным циклом потоков и для выполнения задач. Он является частью фреймворка java.util.concurrent и предоставляет гибкие способы работы с потоками.


Основные методы ExecutorService

submit(Runnable task): Отправляет задачу на выполнение и возвращает объект Future, представляющий эту задачу.
submit(Callable<T> task): Отправляет задачу, возвращающую результат, на выполнение и возвращает объект Future.
invokeAll(Collection<? extends Callable<T>> tasks): Выполняет все задачи из коллекции и возвращает список объектов Future.
invokeAny(Collection<? extends Callable<T>> tasks): Выполняет все задачи из коллекции и возвращает результат одной из успешно завершенных задач.
shutdown(): Начинает плавное завершение работы пула, позволяя выполнению текущих задач завершиться.
shutdownNow(): Пытается немедленно завершить выполнение всех задач.


Пример использования submit
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class SubmitExample {

public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);

Callable<Integer> task1 = () -> {
Thread.sleep(1000);
return 10;
};

Callable<Integer> task2 = () -> {
Thread.sleep(2000);
return 20;
};

Future<Integer> future1 = executorService.submit(task1);
Future<Integer> future2 = executorService.submit(task2);

try {
System.out.println("Result of task1: " + future1.get());
System.out.println("Result of task2: " + future2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}

executorService.shutdown();
}
}


Пример использования invokeAll и invokeAny
public class InvokeExample {

public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);

List<Callable<Integer>> tasks = Arrays.asList(
() -> {
Thread.sleep(1000);
return 1;
},
() -> {
Thread.sleep(2000);
return 2;
},
() -> {
Thread.sleep(3000);
return 3;
}
);

try {
List<Future<Integer>> results = executorService.invokeAll(tasks);
for (Future<Integer> result : results) {
System.out.println("Result: " + result.get());
}

Integer anyResult = executorService.invokeAny(tasks);
System.out.println("First completed task result: " + anyResult);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}

executorService.shutdown();
}
}


Управление завершением ExecutorService

Правильное завершение ExecutorService важно для предотвращения утечек ресурсов и корректного завершения программы.

shutdown(): Начинает плавное завершение. Новые задачи не принимаются, но уже отправленные задачи продолжают выполнение.
executorService.shutdown();


shutdownNow(): Пытается немедленно завершить все задачи, возвращает список задач, которые не были начаты.
List<Runnable> notExecutedTasks = executorService.shutdownNow();


awaitTermination(long timeout, TimeUnit unit): Ожидает завершения всех задач в течение указанного времени.

try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}


#Java #Training #Multithreading #Medium #ExecutorService