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
Лямбда-выражения

Лямбда-выражения, или просто "лямбды", были введены в Java 8 и представляют собой компактный способ представления анонимных функций. Они позволяют передавать поведение как параметр метода, что делает код более гибким и лаконичным.

Синтаксис лямбда-выражений

Лямбда-выражение состоит из параметров, стрелки (->) и тела. Существует несколько форм записи лямбда-выражений:

Без параметров:
() -> System.out.println("Hello, World!");


С одним параметром (скобки можно опустить):
x -> x * 2;


С несколькими параметрами:
(x, y) -> x + y;


С телом, состоящим из нескольких выражений:
(x, y) -> {
int sum = x + y;
return sum;
};


Использование лямбда-выражений

Лямбда-выражения чаще всего используются в сочетании с функциональными интерфейсами. Функциональный интерфейс — это интерфейс с единственным абстрактным методом (SAM — Single Abstract Method). Примеры таких интерфейсов включают Runnable, Callable, Comparator и интерфейсы из пакета java.util.function.

Пример использования лямбда-выражения с функциональным интерфейсом Runnable:
Runnable r = () -> System.out.println("Lambda Runnable");
new Thread(r).start();


Встроенные функциональные интерфейсы

Java предоставляет ряд встроенных функциональных интерфейсов в пакете java.util.function, которые можно использовать с лямбда-выражениями. Некоторые из них:


Predicate<T>: принимает один аргумент и возвращает логическое значение.
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // true


Consumer<T>: принимает один аргумент и не возвращает результат.
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello, Consumer!");


Function<T, R>: принимает один аргумент и возвращает результат.

Function<Integer, String> intToString = num -> "Number: " + num;
System.out.println(intToString.apply(5)); // Number: 5


Supplier<T>: не принимает аргументов, но возвращает результат.
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get());


UnaryOperator<T>: принимает один аргумент и возвращает результат того же типа.
UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25


BinaryOperator<T>: принимает два аргумента и возвращает результат того же типа.
BinaryOperator<Integer> add = (x, y) -> x + y;
System.out.println(add.apply(2, 3)); // 5


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

public class ArithmeticExample {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 5;

int result = (a * b / c) + (a % c * b) - (a - c);

System.out.println(result);
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
0%
65
33%
75
67%
35
0%
95
Действительно, почему?🧐

https://t.me/Java_for_beginner_dev

#Mems
Применение лямбда-выражений в коллекциях и Stream API

Лямбда-выражения и коллекции

Одним из самых мощных применений лямбда-выражений является их использование в коллекциях. С введением лямбд в Java 8, стандартные интерфейсы коллекций были расширены новыми методами, такими как forEach, removeIf, replaceAll и sort, которые принимают лямбда-выражения в качестве параметров.

Метод forEach:
List<String> list = Arrays.asList("a", "b", "c", "d");
list.forEach(element -> System.out.println(element));


Метод removeIf:

List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three", "four"));
list.removeIf(element -> element.length() > 3);
list.forEach(System.out::println); // one, two


Метод replaceAll:
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three", "four"));
list.replaceAll(String::toUpperCase);
list.forEach(System.out::println); // ONE, TWO, THREE, FOUR


Метод sort:

List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three", "four"));
list.sort((s1, s2) -> s1.compareTo(s2));
list.forEach(System.out::println); // four, one, three, two


Лямбда-выражения и Stream API

Stream API в Java 8 позволяет легко и эффективно работать с потоками данных, выполняя операции над элементами коллекций. Лямбда-выражения играют ключевую роль в использовании Stream API.

Создание Stream:
List<String> list = Arrays.asList("one", "two", "three", "four");
Stream<String> stream = list.stream();


Фильтрация (filter):

List<String> list = Arrays.asList("one", "two", "three", "four");
List<String> filteredList = list.stream()
.filter(s -> s.startsWith("t"))
.collect(Collectors.toList());
filteredList.forEach(System.out::println); // two, three


Преобразование (map):

List<String> list = Arrays.asList("one", "two", "three", "four");
List<Integer> lengths = list.stream()
.map(String::length)
.collect(Collectors.toList());
lengths.forEach(System.out::println); // 3, 3, 5, 4


Сортировка (sorted):
List<String> list = Arrays.asList("one", "two", "three", "four");
List<String> sortedList = list.stream()
.sorted()
.collect(Collectors.toList());
sortedList.forEach(System.out::println); // four, one, three, two


Агрегация (reduce):
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println(sum); // 15


Преимущества лямбда-выражений

Компактность и читабельность: Лямбда-выражения позволяют значительно сократить объем кода, особенно при работе с коллекциями и потоками.
Удобство использования: Лямбда-выражения упрощают передачу поведения как параметра и делают код более декларативным.
Функциональное программирование: Лямбда-выражения являются основой функционального программирования в Java, позволяя легко работать с функциями высшего порядка и композициями.


#Java #Training #Medium
Введение в Stream API и основные методы создания стримов

Stream API, введенный в Java 8, предоставляет мощный способ работы с коллекциями данных. Stream представляет собой последовательность элементов, поддерживающую различные операции для создания желаемых результатов. Операции со стримами могут быть промежуточными (возвращающими другой стрим) и терминальными (возвращающими конечный результат).

Создание стримов

Из коллекций:
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();


Из массивов:
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);


Из значений:

Stream<String> stream = Stream.of("a", "b", "c");


Из файлов:
try (Stream<String> stream = Files.lines(Paths.get("file.txt"))) {
stream.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}


Бесконечные стримы:

iterate:
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);
stream.forEach(System.out::println);


generate:
Stream<Double> stream = Stream.generate(Math::random).limit(10);
stream.forEach(System.out::println);



Промежуточные методы Stream API

Промежуточные методы возвращают новый стрим, позволяя строить цепочки операций.


filter:
List<String> list = Arrays.asList("a", "ab", "abc", "abcd");
list.stream()
.filter(s -> s.length() > 2)
.forEach(System.out::println); // abc, abcd


map:
List<String> list = Arrays.asList("1", "2", "3");
list.stream()
.map(Integer::parseInt)
.forEach(System.out::println); // 1, 2, 3


flatMap:
List<List<String>> list = Arrays.asList(
Arrays.asList("a", "b"),
Arrays.asList("c", "d")
);
list.stream()
.flatMap(Collection::stream)
.forEach(System.out::println); // a, b, c, d


distinct:
List<String> list = Arrays.asList("a", "b", "a", "c", "b");
list.stream()
.distinct()
.forEach(System.out::println); // a, b, c


sorted:

Без компаратора:
List<String> list = Arrays.asList("c", "a", "b");
list.stream()
.sorted()
.forEach(System.out::println); // a, b, c


С компаратором:
List<String> list = Arrays.asList("c", "a", "b");
list.stream()
.sorted(Comparator.reverseOrder())
.forEach(System.out::println); // c, b, a


peek:
List<String> list = Arrays.asList("a", "b", "c");
list.stream()
.peek(System.out::println)
.map(String::toUpperCase)
.forEach(System.out::println); // a, A, b, B, c, C


limit:
Stream<Integer> stream = Stream.iterate(1, n -> n + 1);
stream.limit(5)
.forEach(System.out::println); // 1, 2, 3, 4, 5


skip:
Stream<Integer> stream = Stream.iterate(1, n -> n + 1);
stream.skip(5)
.limit(5)
.forEach(System.out::println); // 6, 7, 8, 9, 10


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

public class ConcatenationExample {
public static void main(String[] args) {
int x = 5;
int y = 10;
String str1 = "Hello";
String str2 = "World";

String result = str1 + x + y + str2 + (x + y) + str1.length() + (x * y);

System.out.println(result);
}
}


#Tasks
Почему не любят тестировщиков?🤪

https://t.me/Java_for_beginner_dev

#Mems
Терминальные методы Stream API

Терминальные методы завершают цепочку операций со стримами, возвращая результат или выполняя действие.

forEach:
List<String> list = Arrays.asList("a", "b", "c");
list.stream()
.forEach(System.out::println); // a, b, c


collect:
List<String> list = Arrays.asList("a", "b", "c");
List<String> result = list.stream()
.collect(Collectors.toList());


reduce:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream()
.reduce(0, (a, b) -> a + b);


toArray:
List<String> list = Arrays.asList("a", "b", "c");
String[] array = list.stream()
.toArray(String[]::new);


findFirst:
List<String> list = Arrays.asList("a", "b", "c");
Optional<String> first = list.stream()
.findFirst();


findAny:
List<String> list = Arrays.asList("a", "b", "c");
Optional<String> any = list.stream()
.findAny();


count:
List<String> list = Arrays.asList("a", "b", "c");
long count = list.stream()
.count();


anyMatch:
List<String> list = Arrays.asList("a", "b", "c");
boolean anyMatch = list.stream()
.anyMatch(s -> s.equals("a"));


allMatch:
List<String> list = Arrays.asList("a", "b", "c");
boolean allMatch = list.stream()
.allMatch(s -> s.length() == 1);


noneMatch:
List<String> list = Arrays.asList("a", "b", "c");
boolean noneMatch = list.stream()
.noneMatch(s -> s.equals("d"));



Продвинутые методы и примеры использования Stream API

groupingBy:
List<String> list = Arrays.asList("a", "ab", "abc", "abcd");
Map<Integer, List<String>> groupedByLength = list.stream()
.collect(Collectors.groupingBy(String::length));


partitioningBy:
List<String> list = Arrays.asList("a", "ab", "abc", "abcd");
Map<Boolean, List<String>> partitionedByLength = list.stream()
.collect(Collectors.partitioningBy(s -> s.length() > 2));


mapping:
List<String> list = Arrays.asList("a", "ab", "abc", "abcd");
List<Integer> lengths = list.stream()
.collect(Collectors.mapping(String::length, Collectors.toList()));


joining:
List<String> list = Arrays.asList("a", "b", "c");
String joined = list.stream()
.collect(Collectors.joining(", "));


Примеры использования Stream API

Фильтрация и преобразование списка строк:
List<String> list = Arrays.asList("apple", "banana", "cherry", "date");
List<String> result = list.stream()
.filter(s -> s.startsWith("a"))
.map(String::toUpperCase)
.collect(Collectors.toList());


Группировка чисел по четности:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Map<Boolean, List<Integer>> evenOddMap = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));


Подсчет элементов в списке:
List<String> list = Arrays.asList("a", "b", "c", "a", "b", "c");
Map<String, Long> frequencyMap = list.stream()
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));


Нахождение максимального значения в списке:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> maxNumber = numbers.stream()
.max(Integer::compare);


#Java #Training #Stream #Medium
Многопоточность

Многопоточность — это способность программы выполнять несколько потоков одновременно. В Java каждый поток представляет собой отдельную линию выполнения в приложении. Многопоточность позволяет эффективно использовать ресурсы многоядерных процессоров, повышая производительность и отзывчивость программ.

Параллелизм и конкурентность

Параллелизм: выполнение нескольких задач одновременно, например, на многоядерных процессорах.
Конкурентность: выполнение нескольких задач чередованием, для создания иллюзии одновременного выполнения на одноядерных процессорах.


Основные понятия многопоточности

Поток (Thread): наименьшая единица выполнения. Каждый поток выполняет свою задачу независимо.
Процесс: программа, выполняющаяся в отдельной среде. Каждый процесс может содержать несколько потоков.


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

В Java создание потоков может быть реализовано двумя способами: наследованием класса Thread и реализацией интерфейса Runnable.

Наследование класса Thread

Создание класса, наследующего Thread:
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread is running");
}

public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // Запуск потока
}
}


Реализация интерфейса Runnable

Создание класса, реализующего Runnable:
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("MyRunnable is running");
}

public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start(); // Запуск потока
}
}


Методы класса Thread

start(): запускает поток, вызывая метод run().
run(): содержит код, который выполнится в новом потоке.
sleep(long millis): приостанавливает выполнение потока на заданное количество миллисекунд.


public class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(1000); // Поток спит 1 секунду
System.out.println("Thread woke up");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


join(): ожидает завершения выполнения потока.

public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running");
}

public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
try {
thread.join(); // Ожидание завершения потока
System.out.println("Thread has finished");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


isAlive(): возвращает true, если поток выполняется, иначе false.
MyThread thread = new MyThread();
thread.start();
System.out.println(thread.isAlive()); // true, если поток выполняется


Приоритеты потоков


Каждый поток в Java имеет приоритет, который влияет на порядок планирования потоков. Приоритеты варьируются от Thread.MIN_PRIORITY (1) до Thread.MAX_PRIORITY (10). Поток с более высоким приоритетом будет запланирован раньше, чем поток с более низким приоритетом.
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() {
System.out.println(getName() + " is running");
}

public static void main(String[] args) {
MyThread thread1 = new MyThread("Thread 1");
MyThread thread2 = new MyThread("Thread 2");

thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);

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


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

import java.util.HashSet;
import java.util.Set;

public class SetExample {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("apple");
set.add("cherry");
set.add(null);
set.add("banana");

int result = set.size() + (set.contains(null) ? 1 : 0) + (set.contains("apple") ? 1 : 0);

System.out.println(result);
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
10%
5
80%
6
10%
4
0%
3
Если одиноко, вставь autoplay в свою страницу и жди гостей🫣

https://t.me/Java_for_beginner_dev

#Mems
Использование synchronized в многопоточности

Ключевое слово synchronized в Java используется для управления доступом к общим ресурсам несколькими потоками. Оно обеспечивает, что только один поток может выполнить синхронизированный блок или метод в определенный момент времени, предотвращая состояние гонки и обеспечивая корректное выполнение многопоточных программ.

Синхронизированные методы

Синхронизированные методы автоматически захватывают монитор объекта, на котором они вызываются. Это гарантирует, что только один поток может выполнить синхронизированный метод объекта одновременно.
public class Counter {
private int count = 0;

public synchronized void increment() {
count++;
}

public synchronized int getCount() {
return count;
}
}

public class MyThread extends Thread {
private Counter counter;

public MyThread(Counter counter) {
this.counter = counter;
}

@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}

public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
MyThread thread1 = new MyThread(counter);
MyThread thread2 = new MyThread(counter);

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

thread1.join();
thread2.join();

System.out.println("Final count: " + counter.getCount()); // 2000
}
}


Синхронизированные блоки

Синхронизированные блоки позволяют синхронизировать доступ к общим ресурсам более гибко, чем синхронизированные методы. Они позволяют выбрать объект для захвата монитора и синхронизировать только часть кода, что может повысить производительность.
public class Counter {
private int count = 0;
private final Object lock = new Object();

public void increment() {
synchronized (lock) {
count++;
}
}

public int getCount() {
synchronized (lock) {
return count;
}
}
}

public class MyThread extends Thread {
private Counter counter;

public MyThread(Counter counter) {
this.counter = counter;
}

@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}

public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
MyThread thread1 = new MyThread(counter);
MyThread thread2 = new MyThread(counter);

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

thread1.join();
thread2.join();

System.out.println("Final count: " + counter.getCount()); // 2000
}
}


Синхронизация по классу

Синхронизация по классу используется для статических методов и блоков. В этом случае захватывается монитор класса, а не объекта.
public class StaticCounter {
private static int count = 0;

public static synchronized void increment() {
count++;
}

public static synchronized int getCount() {
return count;
}
}

public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
StaticCounter.increment();
}
}

public static void main(String[] args) throws InterruptedException {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();

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

thread1.join();
thread2.join();

System.out.println("Final count: " + StaticCounter.getCount()); // 2000
}
}
Mutex и Lock

В Java Lock предоставляется в пакете java.util.concurrent.locks. Это более мощный инструмент синхронизации, чем synchronized, предлагающий больше возможностей, таких как попытка захвата блокировки без ожидания, прерывание во время ожидания блокировки и использование условных переменных.

Интерфейс Lock

Интерфейс Lock предоставляет основные методы для управления блокировкой.

lock():
Захватывает блокировку.
unlock(): Освобождает блокировку.
tryLock(): Попытка захвата блокировки без ожидания.
lockInterruptibly(): Захват блокировки с возможностью прерывания.


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


ReentrantLock — это реализация интерфейса Lock, позволяющая потоку захватить блокировку несколько раз.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();

public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}

public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}

public class MyThread extends Thread {
private Counter counter;

public MyThread(Counter counter) {
this.counter = counter;
}

@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}

public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
MyThread thread1 = new MyThread(counter);
MyThread thread2 = new MyThread(counter);

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

thread1.join();
thread2.join();

System.out.println("Final count: " + counter.getCount()); // 2000
}
}


Использование tryLock()

Метод tryLock() пытается захватить блокировку немедленно, возвращая true, если это удалось, и false в противном случае.
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();

public void increment() {
if (lock.tryLock()) {
try {
count++;
} finally {
lock.unlock();
}
} else {
System.out.println("Unable to lock");
}
}

public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}

public class MyThread extends Thread {
private Counter counter;

public MyThread(Counter counter) {
this.counter = counter;
}

@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}

public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
MyThread thread1 = new MyThread(counter);
MyThread thread2 = new MyThread(counter);

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

thread1.join();
thread2.join();

System.out.println("Final count: " + counter.getCount()); // 2000
}
}
Условные переменные

Condition — это интерфейс, предоставляющий средства для блокировки потоков до тех пор, пока они не будут сигнализировать другим потокам. Это более гибкая альтернатива методу wait() и notify() в синхронизированных блоках.
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BoundedBuffer {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();

private final Object[] items = new Object[100];
private int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) {
notFull.await();
}
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}

public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await();
}
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
count--;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}


#Java #Training #Multithreading #Medium
Многопоточность в Java: wait и notify

В Java методы wait(), notify(), и notifyAll() используются для управления потоками в многопоточной среде. Они позволяют потокам координировать свои действия при доступе к общим ресурсам, предоставляя механизм для ожидания и уведомления. Эти методы определены в классе Object и должны вызываться из синхронизированного контекста.

Метод wait()

Метод wait() заставляет текущий поток ждать до тех пор, пока другой поток не вызовет метод notify() или notifyAll() для этого объекта. Поток, вызвавший wait(), освобождает монитор и переходит в состояние ожидания.


Пример использования wait():
public class WaitNotifyExample {
private final Object lock = new Object();
private boolean condition = false;

public void doWait() {
synchronized (lock) {
while (!condition) {
try {
System.out.println("Waiting...");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Condition met!");
}
}

public void doNotify() {
synchronized (lock) {
condition = true;
lock.notify();
System.out.println("Notified!");
}
}

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

Thread waiter = new Thread(example::doWait);
Thread notifier = new Thread(example::doNotify);

waiter.start();
try {
Thread.sleep(1000); // Задержка для демонстрации ожидания
} catch (InterruptedException e) {
e.printStackTrace();
}
notifier.start();
}
}


Метод notify()

Метод notify() пробуждает один из потоков, ожидающих этого объекта. Выбор потока, который будет пробужден, не определяется, поэтому для пробуждения всех ожидающих потоков используется метод notifyAll().

Пример использования notify():
public class WaitNotifyExample {
private final Object lock = new Object();
private boolean condition = false;

public void doWait() {
synchronized (lock) {
while (!condition) {
try {
System.out.println("Waiting...");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Condition met!");
}
}

public void doNotify() {
synchronized (lock) {
condition = true;
lock.notify();
System.out.println("Notified!");
}
}

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

Thread waiter = new Thread(example::doWait);
Thread notifier = new Thread(example::doNotify);

waiter.start();
try {
Thread.sleep(1000); // Задержка для демонстрации ожидания
} catch (InterruptedException e) {
e.printStackTrace();
}
notifier.start();
}
}


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

public class ArrayChallenge {
public static void main(String[] args) {
int[][] array = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};

int sum = 0;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
if (i == j || i + j == array.length - 1) {
sum += array[i][j];
}
}
}

System.out.println(sum);
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
9%
15
18%
20
55%
25
18%
30
Ты 🫵 знаешь что делать!🤪

https://t.me/Java_for_beginner_dev

#Mems