Java for Beginner
871 subscribers
1.01K photos
275 videos
14 files
1.68K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Что выведет код?

import java.util.concurrent.CountDownLatch;

public class Task140726 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
latch.countDown();

for (int i = 0; i < 3; i++) {
latch.await();
System.out.print(i + " ");
}
System.out.print(latch.getCount());
}
}


#Tasks
👍3
👍3
Что такое AsynchronousFileChannel и AsynchronousSocketChannel? 🤓

Ответ:

Асинхронные каналы в NIO.2 (Java 7). AsynchronousFileChannel позволяет выполнять операции чтения/записи неблокирующе, передавая CompletionHandler или используя Future. 

AsynchronousSocketChannel — аналогично для сетевых сокетов. Они используют пулы потоков (AsynchronousChannelGroup) и позволяют работать с высокой нагрузкой без создания множества потоков. Применяются в высокопроизводительных серверах, хотя сейчас чаще используют Netty или NIO + селекторы.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
По-моему это шедевр...
🤣4
История технологии сегодня — 15 июля

ℹ️ Кто родился в этот день

Рем Ви́кторович Хохло́в (15 июля 1926, Ливны, Орловская губерния — 8 августа 1977, Москва) — советский физик, один из основоположников нелинейной оптики. Признание в научном мире Хохлову принесли два изданных в 1961 году труда: «К теории ударных радиоволн в нелинейных линиях» и «О распространении волн в нелинейных диспергирующих линиях». Первая монография заложила основы теоретической нелинейной акустики, вторая сформировала основание теории распространения электромагнитных волн в нелинейных средах.

Леон Макс Ле́дерман (англ. Leon Max Lederman; 15 июля 1922, Нью-Йорк, США — 3 октября 2018, Рексберг, Айдахо, США) — американский физик-экс­пе­ри­мен­та­тор, специалист в области физики высоких энергий, нобелевский лауреат 1988 года за открытие мюонного нейтрино.

Бертрам Невилл Брокха́уз (англ. Bertram Neville Brockhouse; 15 июля 1918, Летбридж, Канада — 13 октября 2003, Гамильтон, Канада) — канадский учёный, физик-ядерщик, лауреат Нобелевской премии по физике в 1994 г. «за создание нейтронной спектроскопии» (совместно с Клиффордом Шаллом).


🌐 Знаковые события

1687 — Исаак Ньютон опубликовал в Лондоне «Philosophiae naturalis principia mathematica» с изложением открытий автора в области прикладной математики, астрономии и физики, в том числе и теорию всемирного тяготения (5 июля по ст.ст.[8]).

2006 — основан сервис микроблоггинга Twitter.

1983 — в Японии вышли игровые приставки NES и SG-1000.


#Biography #Birth_Date #Events #15июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)

Глава 2. Современный NIO.2 (java.nio.file)

Перемещение и переименование файлов и директорий

Перемещение и удаление — это операции, которые завершают жизненный цикл файла в файловой системе.

В отличие от копирования, которое создаёт новую копию данных, перемещение часто не требует физического перемещения байтов на диске — достаточно изменить метаданные в файловой системе.

Удаление, в свою очередь, освобождает inode и блоки данных, но реальное освобождение дискового пространства зависит от того, есть ли другие ссылки на те же данные. Класс Files предоставляет атомарные и безопасные методы для этих операций, каждый из которых имеет чёткую семантику ошибок.

inode (index node) — это структура данных в файловых системах Unix, хранящая метаданные файла: права доступа, владелец, размер, временные метки и указатели на блоки данных. Имя файла — это просто ссылка на inode в директории.


Files.move(Path source, Path target) — перемещение и переименование

Метод Files.move(Path source, Path target, CopyOption... options) выполняет перемещение файла или директории из source в target. На уровне файловой системы это может быть либо переименование в пределах одного раздела (атомарная операция, изменяющая только записи в директориях), либо копирование с последующим удалением источника (если источник и назначение на разных разделах или файловых системах).
import java.nio.file.*;
import java.io.IOException;

public class FileMover {

public void renameFile(Path oldName, Path newName) throws IOException {
// Простое переименование в той же директории
Files.move(oldName, newName);
}

public void moveToArchive(Path file, Path archiveDir) throws IOException {
// Перемещение в другую директорию
Path target = archiveDir.resolve(file.getFileName());
Files.move(file, target);
}
}



Разница между копированием и перемещением

Копирование (Files.copy) создаёт независимую копию данных. После копирования источник и назначение — это два разных файла с разными inode, каждый занимает своё дисковое пространство. Изменение одного не влияет на другой.

Перемещение в пределах одного FileStore (одного раздела диска) — это операция над метаданными. Файловая система изменяет запись в родительской директории источника (удаляет ссылку на inode) и добавляет запись в родительскую директорию назначения. Сами данные на диске не перемещаются. inode остаётся тем же, временные метки данных не меняются. Это выполняется за один системный вызов rename() и является атомарным.

Перемещение между разными FileStore (разные разделы, разные диски, сетевые ФС) невозможно как атомарная операция над метаданными. JVM выполняет копирование данных из источника в назначение, а затем удаляет источник. Это неатомарно: если копирование прошло успешно, но удаление источника не удалось (например, файл заблокирован), вы получите частичный результат — данные продублированы, а исходный файл остался на месте.

FileStore — абстракция над хранилищем файловой системы (раздел диска, том, сетевой ресурс). Каждый раздел имеет свой FileStore, и перемещение между разными FileStore требует физического копирования данных.


Опции перемещения


StandardCopyOption.REPLACE_EXISTING
Разрешает перезапись существующего файла или директории в точке назначения. Без этой опции Files.move() выбросит FileAlreadyExistsException, если target уже существует.
Path src = Paths.get("/data/report_v1.txt");
Path dst = Paths.get("/data/report.txt");

// Перезаписываем старый отчёт новым
Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING);

Важно: при перезаписи директории целевая директория должна быть пустой. Если target — непустая директория, даже с REPLACE_EXISTING будет выброшено DirectoryNotEmptyException.

StandardCopyOption.ATOMIC_MOVE
Выполняет атомарное перемещение. Это означает, что операция либо завершится целиком, либо не произойдёт вообще. Другие процессы никогда не увидят промежуточного состояния, где ни источник, ни назначение не существуют.
Path src = Paths.get("/data/temp_report.txt");
Path dst = Paths.get("/data/report.txt");

// Атомарное переименование: читатели либо видят старый файл, либо новый
Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE);


Ограничения ATOMIC_MOVE:
Источник и назначение должны находиться на одном FileStore. Если это не так, выбрасывается AtomicMoveNotSupportedException.
Нельзя комбинировать с COPY_ATTRIBUTES — атомарное перемещение не копирует атрибуты, оно переносит inode целиком, вместе со всеми его метаданными.
Если назначение существует и передан REPLACE_EXISTING, перезапись тоже будет атомарной.

Атомарность в контексте файловых систем означает неделимость операции. Для rename() атомарность гарантируется на уровне ядра ОС: запись в структуры директорий защищена блокировками, и другие процессы видят либо старое имя, либо новое, но не промежуточное состояние.

Пример: перемещение обработанного файла в архивную папку
import java.nio.file.*;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

public class ArchiveMover {

private static final DateTimeFormatter DATE_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault());

public void archiveProcessedFile(Path processedFile, Path archiveRoot) throws IOException {
// Проверяем, что источник существует и является файлом
if (!Files.isRegularFile(processedFile)) {
throw new NoSuchFileException("Файл не найден или не является обычным файлом: " + processedFile);
}

// Создаём архивную директорию с текущей датой: /archive/2024-07-14/
String today = DATE_FORMAT.format(Instant.now());
Path datedArchive = archiveRoot.resolve(today);
Files.createDirectories(datedArchive);

// Формируем целевой путь
String fileName = processedFile.getFileName().toString();
Path target = datedArchive.resolve(fileName);

// Если файл с таким именем уже есть в архиве — добавляем счётчик
Path finalTarget = resolveUniqueName(target);

// Атомарное перемещение
Files.move(processedFile, finalTarget, StandardCopyOption.ATOMIC_MOVE);

System.out.println("Файл перемещён в архив: " + finalTarget.toAbsolutePath());
}

private Path resolveUniqueName(Path target) {
if (Files.notExists(target)) {
return target;
}

// Генерируем уникальное имя: file.txt -> file_1.txt, file_2.txt
String fileName = target.getFileName().toString();
int lastDot = fileName.lastIndexOf('.');
String base = lastDot > 0 ? fileName.substring(0, lastDot) : fileName;
String ext = lastDot > 0 ? fileName.substring(lastDot) : "";
Path parent = target.getParent();

int counter = 1;
Path candidate;
do {
candidate = parent.resolve(base + "_" + counter + ext);
counter++;
} while (Files.exists(candidate));

return candidate;
}

public static void main(String[] args) {
try {
Path file = Paths.get("/app/inbox/order_12345.xml");
Path archive = Paths.get("/app/archive");
new ArchiveMover().archiveProcessedFile(file, archive);
} catch (AtomicMoveNotSupportedException e) {
System.err.println("Атомарное перемещение невозможно — источник и архив на разных разделах");
// Fallback: копирование + удаление
} catch (IOException e) {
System.err.println("Ошибка архивирования: " + e.getMessage());
}
}
}

В этом примере ATOMIC_MOVE гарантирует, что в процессе перемещения читатели директории inbox либо видят файл на месте, либо не видят его вообще (уже в архиве). Промежуточного состояния "файл исчез" не возникает. Если архив находится на другом разделе, ловим AtomicMoveNotSupportedException и применяем fallback-стратегию.


#Java #для_новичков #beginner #IO #NIO #Files #move #delete
👍5
Удаление файлов и директорий

Files.delete(Path) — строгое удаление

Метод Files.delete(Path path) удаляет файл, директорию или символическую ссылку. Если объект не существует — выбрасывает NoSuchFileException. Если путь — непустая директория — выбрасывает DirectoryNotEmptyException. Если нет прав на удаление — AccessDeniedException.
Path file = Paths.get("/tmp/old_data.tmp");
Files.delete(file); // NoSuchFileException, если файла нет


Строгость этого метода полезна, когда вы ожидаете, что файл существует, и хотите знать, если это не так. Например, при очистке временного файла, который обязательно должен был создаться предыдущим шагом.


Files.deleteIfExists(Path) — условное удаление

Метод Files.deleteIfExists(Path path) удаляет файл, если он существует. Возвращает true, если файл был удалён, false — если файла не было. Не выбрасывает исключение при отсутствии файла, но всё ещё выбрасывает DirectoryNotEmptyException и AccessDeniedException.
Path tempFile = Paths.get("/tmp/session_abc123.tmp");
boolean wasDeleted = Files.deleteIfExists(tempFile);
if (wasDeleted) {
System.out.println("Временный файл удалён");
} else {
System.out.println("Файл уже был удалён или не существовал");
}

Этот метод идеален для идемпотентной очистки: можно вызывать многократно без ошибок.

DirectoryNotEmptyException

Оба метода delete() и deleteIfExists() не удаляют непустые директории. Это ограничение на уровне файловой системы: удаление директории требует сначала удалить все её содержимое. Файловая система хранит список записей в директории, и ядро ОС отказывает в удалении, пока список не пуст.
Path dir = Paths.get("/tmp/myapp");
try {
Files.delete(dir); // DirectoryNotEmptyException, если внутри есть файлы
} catch (DirectoryNotEmptyException e) {
// Нужно сначала удалить содержимое
}



Обход дерева с удалением всех файлов


Для рекурсивного удаления директории со всем содержимым используется обход дерева в обратном порядке: сначала удаляются файлы и пустые поддиректории на самом глубоком уровне, затем — родительские директории.
import java.nio.file.*;
import java.io.IOException;
import java.util.Comparator;
import java.util.stream.Stream;

public class TreeDeleter {

public void deleteDirectoryRecursively(Path root) throws IOException {
if (!Files.exists(root)) {
return; // Нечего удалять
}

// walk возвращает Stream<Path> в порядке pre-order (родитель до детей)
// sorted(reverseOrder()) переворачивает: сначала глубокие пути, потом корень
try (Stream<Path> walk = Files.walk(root)) {
walk.sorted(Comparator.reverseOrder())
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
// Оборачиваем в UncheckedIOException для использования в forEach
throw new UncheckedIOException(
"Не удалось удалить: " + path, e
);
}
});
} catch (UncheckedIOException e) {
// Распаковываем обратно в checked исключение
throw e.getCause();
}
}
}


Почему Comparator.reverseOrder()? Метод Files.walk() обходит дерево в порядке pre-order: сначала корень, потом его дети. Если мы будем удалять в этом порядке, попытка удалить корневую директорию первой выбросит DirectoryNotEmptyException, так как дети ещё не удалены. reverseOrder() сортирует пути по строковому представлению в обратном порядке, что гарантирует, что более глубокие пути (с большим количеством сегментов) обрабатываются раньше.

UncheckedIOException — это обёртка вокруг IOException, наследующая RuntimeException. Она нужна потому, что функциональные интерфейсы в Java (например, Consumer в forEach) не могут выбрасывать checked-исключения. Оборачивая IOException в UncheckedIOException, мы пробрасываем его через лямбду, а затем распаковываем вне.

Пример: удаление всех .tmp файлов во временной папке
import java.nio.file.*;
import java.io.IOException;
import java.util.stream.Stream;

public class TempCleaner {

private static final Path TEMP_DIR = Paths.get(System.getProperty("java.io.tmpdir"));

public void cleanTempFiles() throws IOException {
// Проверяем, что TEMP_DIR существует и является директорией
if (!Files.isDirectory(TEMP_DIR)) {
throw new NotDirectoryException(TEMP_DIR.toString());
}

// Обходим только верхний уровень временной директории
// newDirectoryStream — более лёгкая альтернатива walk для одного уровня
try (DirectoryStream<Path> stream = Files.newDirectoryStream(TEMP_DIR)) {
for (Path entry : stream) {
if (Files.isRegularFile(entry) && entry.toString().endsWith(".tmp")) {
try {
Files.delete(entry);
System.out.println("Удалён: " + entry.getFileName());
} catch (IOException e) {
// Логируем, но продолжаем удаление остальных
System.err.println("Не удалось удалить " + entry.getFileName()
+ ": " + e.getMessage());
}
}
}
}
}

// Альтернатива: удаление .tmp файлов рекурсивно во всём поддереве
public void cleanTempFilesRecursive(Path root) throws IOException {
try (Stream<Path> walk = Files.walk(root)) {
walk.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".tmp"))
.forEach(p -> {
try {
Files.delete(p);
System.out.println("Удалён: " + p);
} catch (IOException e) {
System.err.println("Ошибка удаления " + p + ": " + e.getMessage());
}
});
}
}

// Удаление .tmp файлов старше N дней
public void cleanOldTempFiles(int daysOld) throws IOException {
long cutoff = System.currentTimeMillis() - (daysOld * 24 * 60 * 60 * 1000L);

try (Stream<Path> walk = Files.walk(TEMP_DIR)) {
walk.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".tmp"))
.filter(p -> {
try {
return Files.getLastModifiedTime(p).toMillis() < cutoff;
} catch (IOException e) {
return false; // Не удалось прочитать — пропускаем
}
})
.forEach(p -> {
try {
Files.delete(p);
System.out.println("Удалён старый файл: " + p.getFileName());
} catch (IOException e) {
System.err.println("Ошибка: " + e.getMessage());
}
});
}
}

public static void main(String[] args) {
try {
TempCleaner cleaner = new TempCleaner();
cleaner.cleanTempFiles();
cleaner.cleanOldTempFiles(7); // удалить .tmp старше 7 дней
} catch (IOException e) {
System.err.println("Ошибка очистки: " + e.getMessage());
}
}
}

В этом примере newDirectoryStream() — более лёгкая альтернатива Files.walk() для обхода только одного уровня. Она не строит дерево и не буферизует пути, а возвращает итератор, который читает директорию порциями. Это экономит память при работе с директориями, содержащими миллионы файлов.

DirectoryStream — это интерфейс в NIO.2, представляющий поток записей директории. В отличие от Stream<Path>, он не поддерживает промежуточные операции (filter, map), но эффективнее для простого обхода, так как не создаёт промежуточных объектов потока.


#Java #для_новичков #beginner #IO #NIO #Files #move #delete
👍6
Путь байтов в памяти JVM при перемещении и удалении

Перемещение: путь данных

При Files.move(source, target, ATOMIC_MOVE):
Объекты source и target — это Path в куче. Их строковые представления извлекаются и кодируются в байты пути в нативной кодировке.
Если источник и назначение на одном FileStore, выполняется системный вызов rename() (POSIX) или MoveFileEx() (Windows). Этот вызов работает исключительно с метаданными файловой системы:
Удаляется запись в родительской директории источника.
Создаётся запись в родительской директории назначения, указывающая на тот же inode.
Блоки данных на диске не перемещаются.
Поскольку данные не копируются, в куче JVM не создаётся никаких буферов или массивов байтов. Объекты source и target остаются в куче как обычные объекты Path. Если они локальные переменные метода, они становятся мусором после выхода из метода и собираются при следующей Minor GC.
Если REPLACE_EXISTING передан и целевой файл существует, файловая система сначала удаляет целевой файл (уменьшает счётчик ссылок на его inode), а затем выполняет rename(). Это тоже атомарно на уровне ядра.
Если источник и назначение на разных FileStore, ATOMIC_MOVE выбросит AtomicMoveNotSupportedException до выполнения каких-либо операций. Без ATOMIC_MOVE JVM выполняет fallback: копирование через буфер в куче + удаление источника.

При этом:
Создаётся буфер byte[] в Young Generation (обычно 8–32 КБ).
Данные копируются блоками: чтение в буфер, запись из буфера.
После успешного копирования источник удаляется через unlink().
Буфер становится мусором и собирается Minor GC.


Удаление: путь данных
При Files.delete(path):
Объект Path в куче преобразуется в байты пути в нативной памяти.
Выполняется системный вызов unlink() (POSIX) или DeleteFile() (Windows). Ядро ОС:
Удаляет запись в родительской директории.
Уменьшает счётчик ссылок (reference count) на inode на 1.
Если счётчик ссылок достигает 0 и нет открытых файловых дескрипторов на этот inode, блоки данных помечаются как свободные.
Если счётчик 0, но дескрипторы открыты (например, другой процесс читает файл), блоки остаются занятыми до закрытия всех дескрипторов. Файл становится "невидимым" в файловой системе, но продолжает занимать место на диске.
Никаких новых объектов в куче JVM не создаётся. Операция полностью работает с метаданными файловой системы.
При удалении директории через rmdir() (POSIX) ядро проверяет, что директория содержит только записи . и .. (текущая и родительская). Если есть другие записи — возвращается ENOTEMPTY, который JVM преобразует в DirectoryNotEmptyException.

Рекурсивное удаление: путь данных и GC
При рекурсивном удалении через Files.walk():
Files.walk(root) создаёт Stream<Path>, который лениво обходит дерево. Каждый вызов walk() порождает внутренние объекты итератора директории в куче.
sorted(Comparator.reverseOrder()) материализует весь поток в список для сортировки. Это означает, что все пути дерева загружаются в память одновременно. Для дерева из миллиона файлов это создаст миллион объектов Path в куче, что может привести к OutOfMemoryError.

Альтернатива без материализации — обход вручную через FileVisitor:
public void deleteRecursivelySafe(Path root) throws IOException {
// FileVisitor — интерфейс для обхода дерева с callback-методами
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException {
if (exc != null) {
throw exc; // ошибка при обходе поддиректории
}
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}

FileVisitor — это интерфейс в NIO.2, определяющий callback-методы для обхода дерева: preVisitDirectory (перед входом в директорию), visitFile (при обнаружении файла), visitFileFailed (при ошибке доступа к файлу), postVisitDirectory (после выхода из директории). SimpleFileVisitor — адаптер с пустыми реализациями по умолчанию.

Files.walkFileTree() не материализует всё дерево в память. Он обходит директории рекурсивно, вызывая callback'и по мере продвижения. Это безопасно для огромных деревьев, так как в памяти хранится только стек текущего пути обхода, а не всё дерево целиком. Объекты Path создаются и сразу становятся мусором после выхода из callback'а, собираясь Minor GC.


#Java #для_новичков #beginner #IO #NIO #Files #move #delete
👍5
Что выведет код?

import java.nio.file.*;
import java.io.*;

public class Task150726 {
public static void main(String[] args) throws IOException {
Path source = Paths.get("file.txt");
Path target = Paths.get("target.txt");

Files.createFile(source);
Files.createFile(target);

try {
Files.move(source, target);
System.out.println("Moved");
} catch (IOException e) {
System.out.println(e.getClass().getSimpleName());
}

System.out.println(Files.exists(source));
}
}


#Tasks
👍4
Что такое FileAttribute и PosixFileAttributes в NIO? 🤓

Ответ:

В NIO.2 можно получать атрибуты файлов без обращения к ОС через Files.getFileAttributeView(). 

FileAttribute — общий интерфейс для любого атрибута (например, "posix:permissions"). PosixFileAttributes расширяет его для UNIX-систем, давая доступ к владельцу, группе, правам доступа (rwx).

Использование: Files.readAttributes(path, PosixFileAttributes.class).

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



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
Привет)

Ничего нового, просто хотел выразить свою благодарность той паре-тройке человек, которые ставят лайки на каждом посте в канале. 🙏

Вы лучшие, спасибо! ❤️
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15
История технологии сегодня — 16 июля

ℹ️ Кто родился в этот день

Бори́с Васи́льевич Бу́нкин (1922—2007) — советский и российский учёный, конструктор и организатор производства зенитных ракетных комплексов для ПВО. С 1968 по 1998 год — генеральный конструктор предприятия НПО «Алмаз», осуществляющего разработку и серийное производство зенитных ракетных комплексов, составляющих основу вооружения отечественных войск ПВОС-75С-125С-300С-400.

Фриц Цернике (нид. Frits Zernike; 16 июля 1888 — 10 марта 1966)голландский физик, лауреат Нобелевской премии по физике 1953 года «За обоснование фазово-контрастного метода, особенно за изобретение фазово-контрастного микроскопа».


🌐 Знаковые события

1867 — Француз Жозеф Монье, бывший садовник, получил патент на армированный бетон, ставший главным материалом при возведении высотных зданий.

1969 — с космодрома на мысе Канаверал стартовала РН «Сатурн-V» с космическим кораблём «Аполлон-11». Через несколько дней посадочный модуль «Орёл» совершит первую пилотируемую посадку на Луну.

1993 — в Лондоне прошла презентация Amiga CD32 — первой 32-битной игровой консоли, использующей CD-ROM.

2000 — принято решение об увеличении числа доменов Интернета.


#Biography #Birth_Date #Events #16июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
[Совет по Java #061]

Тема: Thread.yield() — подсказка планировщику, но может быть проигнорирована. Не используйте для синхронизации.

Проблема: Метод Thread.yield() призван сигнализировать планировщику потоков, что текущий поток готов уступить процессорное время другим потокам.

Однако это всего лишь подсказка (hint) для JVM и операционной системы. Планировщик может проигнорировать её, особенно в средах с вытесняющей многозадачностью, где решение о переключении принимается системой.

Некоторые разработчики используют yield() в циклах ожидания (busy-wait) для "уменьшения нагрузки" на процессор, полагая, что это предотвратит зависание. Это ошибочная практика: она не гарантирует прогресс, не обеспечивает взаимное исключение и не защищает от гонок данных. Более того, в многопроцессорных системах вызов yield() может вообще не изменить поведение, так как другие потоки могут выполняться на других ядрах.

Использование yield() для синхронизации — это антипаттерн, ведущий к недетерминированным ошибкам, которые проявляются только под нагрузкой или на определённых конфигурациях железа.

Решение: Для координации потоков используйте механизмы синхронизации из java.util.concurrent или встроенные synchronizedwait()/notify().

Если нужно временно освободить процессор, применяйте Thread.sleep(1) с минимальной задержкой — это более предсказуемый способ, хотя тоже не идеален.

Для реализации эффективного ожидания используйте LockSupport.park() или Condition.await(). Для уступки в вычислительных циклах с интенсивным использованием CPU можно использовать yield() в исключительных случаях, но только после тщательного анализа и с чётким пониманием, что это не влияет на корректность.

В подавляющем большинстве сценариев yield() не нужен, а его использование свидетельствует о попытке "замаскировать" проблемы синхронизации.
public class ThreadYield {
private boolean flag = false;

//Антипаттерн: yield() в цикле ожидания
public void badWait() {
while (!flag) {
Thread.yield(); // Плохо: может никогда не сработать, а если сработает, то не факт
}
// Действие после флага
}

//Правильно: использование Lock или wait/notify
private final Object lock = new Object();

public void goodWait() throws InterruptedException {
synchronized (lock) {
while (!flag) {
lock.wait(); // Блокируется до уведомления
}
}
}

public void setFlagTrue() {
synchronized (lock) {
flag = true;
lock.notifyAll();
}
}
}


Объяснение:
 Спецификация Java не даёт никаких гарантий относительно того, что yield() действительно вызовет переключение контекста.

Разные реализации JVM и операционные системы трактуют его по-разному: на Windows это обычно приводит к немедленному переключению, на Linux может быть проигнорировано.

Полагаться на него для синхронизации — значит внести в код фактор случайности. Правильные механизмы (wait/notify, Lock, Semaphore, CountDownLatch) обеспечивают детерминированное освобождение ресурсов и гарантируют, что поток будет возобновлён только когда условие действительно выполнено.

Даже Thread.sleep(0) часто работает аналогично yield(), но тоже не рекомендуется.


#Java #советы
👍6
Потом удивляемся почему нет рабочих мест для погромистов. Грууусть...
🗿5🤓2
Что выведет код?

public class Task160726 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.print("A");
Thread.yield();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.print("B");
Thread.yield();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
}
}


#Tasks
👍3
Что такое ProcessHandle в Java 9? 🤓

Ответ:

ProcessHandle (Java 9)
— это улучшенный API для управления процессами ОС из Java.

Позволяет получить дескриптор текущего процесса (ProcessHandle.current()), получить PID, родительский процесс, дочерние процессы, а также завершить процесс (destroy(), destroyForcibly()).

Можно также подписаться на завершение процесса через onExit(), возвращающее CompletableFuture<ProcessHandle>. Это улучшение по сравнению с устаревшим Runtime.exec() и Process позволяет более удобно управлять внешними процессами.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
История технологии сегодня — 17 июля

ℹ️ Кто родился в этот день

Серге́й Константи́нович Годуно́в (17 июля 1929, Москва — 15 июля 2023, Новосибирск) — советский и российский учёный в области математики и механикиакадемик РАН (1994; член-корреспондент АН СССР с 1976). Работы по теории обыкновенных дифференциальных уравненийуравнений в частных производныхвычислительной математикемеханике сплошных средлинейной алгебре.

Гордон Гулд (17 июля 1920, Манхэттен, Нью-Йорк — 16 сентября 2005, Манхэттен, Нью-Йорк) — американский физик, которому широко приписывается изобретение лазера. Гулд первым предложил использовать термин лазер (англ. laser), представляющий собой акроним от английского Light Amplification by Stimulated Emission of Radiation, что в переводе означает усиление света посредством вынужденного излучения.

Жорж Леме́тр (полное имя — Жорж Анри́ Жозе́ф Эдуа́р Леме́тр, фр. Georges Henri Joseph Édouard Lemaître); 17 июля 1894, Шарлеруа, Эно — 20 июня 1966, Лёвен, Брабант) — бельгийский священникастрофизиккосмолог и математик, автор теории Большого взрыва. Один из наиболее влиятельных астрофизиков XX века.


🌐 Знаковые события

1975 — состоялась стыковка космических кораблей «Союз» (СССР) и «Аполлон» (США).


#Biography #Birth_Date #Events #17июля
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Раздел 11. Работа с файлами, I/O и сетью (NIO.2)

Глава 2. Современный NIO.2 (java.nio.file)

Атрибуты файлов — BasicFileAttributes

Каждый файл или директория в файловой системе хранит не только данные, но и метаданные — информацию о самом файле. Эти метаданные называются атрибутами файла и включают размер, временные метки (когда файл создан, изменён или к нему обращались), тип файла (обычный файл, директория, символическая ссылка) и права доступа. В Java NIO.2 работа с атрибутами вынесена в отдельную иерархию интерфейсов, корнем которой является BasicFileAttributes.

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

Интерфейс BasicFileAttributes (пакет java.nio.file.attribute) предоставляет базовый набор атрибутов, общий для всех поддерживаемых JVM файловых систем. Для платформоспецифичных атрибутов существуют расширения: PosixFileAttributes (Unix/Linux/macOS) и DosFileAttributes (Windows). Эта архитектура следует принципу ISP (Interface Segregation Principle, принцип разделения интерфейса): общий интерфейс содержит только универсальные атрибуты, а специфичные вынесены в подинтерфейсы.


Что такое атрибуты файла

Атрибуты файла делятся на несколько категорий:
Базовые атрибуты (BasicFileAttributes) — доступны на всех платформах:
Размер файла (size) — число байтов, занимаемых файлом. Для директорий это платформозависимо и обычно равно размеру записей директории, а не сумме размеров файлов внутри.
Время создания (creationTime) — момент первого создания файла. На некоторых файловых системах может не поддерживаться или совпадать с временем модификации.
Время последней модификации (lastModifiedTime) — момент последнего изменения содержимого файла.
Время последнего доступа (lastAccessTime) — момент последнего чтения файла или выполнения stat().
Тип файла — флаги isDirectory(), isRegularFile(), isSymbolicLink(), isOther().

POSIX-атрибуты (PosixFileAttributes) — специфичны для Unix-подобных систем:
Владелец (owner) — пользователь, владеющий файлом.
Группа (group) — группа пользователей, ассоциированная с файлом.
Права доступа (permissions) — набор битов rwx для владельца, группы и остальных.

DOS-атрибуты (DosFileAttributes) — специфичны для Windows:
Read-only — файл доступен только для чтения.
Hidden — скрытый файл.
System — системный файл.
Archive — флаг архивации.

Временные метки в NIO.2 представлены классом FileTime, а не устаревшим java.util.Date или long. FileTime хранит время с точностью до наносекунд и предоставляет удобные методы конвертации.

Наносекунда — единица времени, равная одной миллиардной доле секунды (10^-9 с). Современные файловые системы (ext4, NTFS, APFS) хранят временные метки с наносекундной точностью, поэтому FileTime поддерживает эту гранулярность, в отличие от Date, который ограничен миллисекундами.


Чтение атрибутов: Files.readAttributes()

Files.readAttributes(Path, Class<A>
Этот метод — основной способ чтения атрибутов файла. Он принимает путь, класс интерфейса атрибутов и опции следования по символическим ссылкам. Возвращает объект, реализующий запрошенный интерфейс.
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.IOException;

public class AttributesReader {

public void printBasicAttributes(Path path) throws IOException {
// Читаем базовые атрибуты
// LinkOption.NOFOLLOW_LINKS — читаем атрибуты самой ссылки, а не цели
BasicFileAttributes attrs = Files.readAttributes(
path,
BasicFileAttributes.class,
LinkOption.NOFOLLOW_LINKS
);

System.out.println("Файл: " + path.getFileName());
System.out.println("Размер: " + attrs.size() + " байт");
System.out.println("Создан: " + attrs.creationTime());
System.out.println("Изменён: " + attrs.lastModifiedTime());
System.out.println("Доступ: " + attrs.lastAccessTime());
System.out.println("Директория: " + attrs.isDirectory());
System.out.println("Обычный файл: " + attrs.isRegularFile());
System.out.println("Символическая ссылка: " + attrs.isSymbolicLink());
}
}

Метод readAttributes() выполняет один системный вызов stat() (POSIX) или GetFileAttributesEx() (Windows), который возвращает структуру со всеми базовыми метаданными. Это эффективнее, чем вызывать отдельные методы Files.size(), Files.isDirectory(), Files.getLastModifiedTime() — каждый из которых выполняет свой системный вызов.

Системный вызов stat() — это вызов ядра Unix, который возвращает структуру stat с полной информацией о файле: inode, размер, права, временные метки, количество ссылок. Аналог на Windows — GetFileAttributesEx().

Различия между readAttributes() и отдельными методами Files
// Неэффективно: три системных вызова
long size = Files.size(path);
boolean isDir = Files.isDirectory(path);
FileTime modified = Files.getLastModifiedTime(path);

// Эффективно: один системный вызов
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
long size = attrs.size();
boolean isDir = attrs.isDirectory();
FileTime modified = attrs.lastModifiedTime();

При интенсивной работе с файловой системой (например, сканирование дерева из миллиона файлов) использование readAttributes() снижает нагрузку на систему в 3–5 раз по сравнению с отдельными вызовами.


#Java #для_новичков #beginner #IO #NIO #Files #BasicFileAttributes
👍4