Аннотации JPA: @OneToOne, @ManyToMany, @JoinColumn, @Lob, @Query, @Modifying, @EnableJpaRepositories
1. @OneToOne
Аннотация @OneToOne используется для определения связи "один к одному" между сущностями. Это может быть, например, связь между пользователем и его профилем.
2. @ManyToMany
@ManyToMany обозначает связь "многие ко многим". Это используется, когда несколько сущностей одной таблицы могут быть связаны с несколькими сущностями другой таблицы.
3. @JoinColumn
Аннотация @JoinColumn задаёт внешний ключ для связи. Она может использоваться с аннотациями @OneToOne и @ManyToOne.
4. @Lob
@Lob используется для маппинга больших объектов, таких как текстовые или бинарные данные.
5. @Query
Аннотация @Query позволяет задавать JPQL или SQL-запросы прямо в репозитории.
6. @Modifying
Используется вместе с @Query для выполнения операций обновления или удаления данных.
7. @EnableJpaRepositories
Эта аннотация активирует JPA-репозитории в Spring Boot.
#Java #Training #Spring #OneToOne #ManyToMany #JoinColumn #Lob #Query #Modifying #EnableJpaRepositories
1. @OneToOne
Аннотация @OneToOne используется для определения связи "один к одному" между сущностями. Это может быть, например, связь между пользователем и его профилем.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "profile_id", referencedColumnName = "id")
private Profile profile;
}
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String bio;
}
Здесь связь между User и Profile устанавливается через внешний ключ profile_id.
2. @ManyToMany
@ManyToMany обозначает связь "многие ко многим". Это используется, когда несколько сущностей одной таблицы могут быть связаны с несколькими сущностями другой таблицы.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<Course> courses;
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToMany(mappedBy = "courses")
private List<Student> students;
}
Связь осуществляется через промежуточную таблицу student_course.
3. @JoinColumn
Аннотация @JoinColumn задаёт внешний ключ для связи. Она может использоваться с аннотациями @OneToOne и @ManyToOne.
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
}
Здесь customer_id в таблице Order будет внешним ключом, ссылающимся на таблицу Customer.
4. @Lob
@Lob используется для маппинга больших объектов, таких как текстовые или бинарные данные.
@Entity
public class Document {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Lob
private String content; // Для больших текстов
@Lob
private byte[] fileData; // Для бинарных данных
}
5. @Query
Аннотация @Query позволяет задавать JPQL или SQL-запросы прямо в репозитории.
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.email = :email")
User findByEmail(@Param("email") String email);
}
6. @Modifying
Используется вместе с @Query для выполнения операций обновления или удаления данных.
public interface UserRepository extends JpaRepository<User, Long> {
@Modifying
@Query("UPDATE User u SET u.name = :name WHERE u.id = :id")
void updateUserName(@Param("id") Long id, @Param("name") String name);
}
7. @EnableJpaRepositories
Эта аннотация активирует JPA-репозитории в Spring Boot.
@Configuration
@EnableJpaRepositories(basePackages = "com.example.repository")
public class AppConfig {
}
#Java #Training #Spring #OneToOne #ManyToMany #JoinColumn #Lob #Query #Modifying #EnableJpaRepositories
Аннотации JPA: @Embeddable, @Embedded, @ElementCollection, @Inheritance, @Cacheable, @Lock, @EntityListeners, @SQLInsert, @SQLUpdate, @SQLDelete
1. @Embeddable и @Embedded
Аннотация @Embeddable обозначает класс, который будет встраиваться в другие сущности, а @Embedded — указывает, что поле встраивает такой класс.
2. @ElementCollection
Используется для хранения коллекций простых типов или встраиваемых объектов.
3. @Inheritance
Настраивает стратегию наследования для сущностей.
4. @Cacheable
Обозначает, что сущность может быть закеширована.
5. @Lock
Используется для управления блокировками данных.
6. @EntityListeners
Позволяет подключать слушатели для отслеживания событий сущности.
7. @SQLInsert, @SQLUpdate, @SQLDelete
Эти аннотации позволяют задать кастомные SQL-запросы для операций вставки, обновления и удаления.
#Java #Training #Spring #Embeddable, #Embedded, #ElementCollection, #Inheritance, #Cacheable, #Lock, #EntityListeners, #SQLInsert, #SQLUpdate, #SQLDelete
1. @Embeddable и @Embedded
Аннотация @Embeddable обозначает класс, который будет встраиваться в другие сущности, а @Embedded — указывает, что поле встраивает такой класс.
@Embeddable
public class Address {
private String street;
private String city;
}
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Embedded
private Address address;
}
2. @ElementCollection
Используется для хранения коллекций простых типов или встраиваемых объектов.
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ElementCollection
private List<String> tags;
}
3. @Inheritance
Настраивает стратегию наследования для сущностей.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Vehicle {
@Id
private Long id;
}
@Entity
public class Car extends Vehicle {
private int seatingCapacity;
}
4. @Cacheable
Обозначает, что сущность может быть закеширована.
@Entity
@Cacheable
public class Product {
@Id
private Long id;
private String name;
}
5. @Lock
Используется для управления блокировками данных.
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT u FROM User u WHERE u.id = :id")
User findForUpdate(@Param("id") Long id);
6. @EntityListeners
Позволяет подключать слушатели для отслеживания событий сущности.
@Entity
@EntityListeners(AuditListener.class)
public class Order {
@Id
private Long id;
}
7. @SQLInsert, @SQLUpdate, @SQLDelete
Эти аннотации позволяют задать кастомные SQL-запросы для операций вставки, обновления и удаления.
@SQLInsert(sql = "INSERT INTO user_audit (id, username) VALUES (?, ?)")
@SQLDelete(sql = "DELETE FROM user_audit WHERE id = ?")
@Entity
public class UserAudit {
@Id
private Long id;
}
#Java #Training #Spring #Embeddable, #Embedded, #ElementCollection, #Inheritance, #Cacheable, #Lock, #EntityListeners, #SQLInsert, #SQLUpdate, #SQLDelete
Что выведет код?
#Tasks
import java.math.BigDecimal;
public class Task201124_1 {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("3");
BigDecimal num2 = new BigDecimal("2");
BigDecimal sum = num1.add(num2);
BigDecimal product = num1.multiply(num2);
BigDecimal result = sum.subtract(product).divide(new BigDecimal("1.0"), 10, BigDecimal.ROUND_HALF_UP);
System.out.println(result);
}
}
#Tasks
Сущности в JPA
В Java Persistence API (JPA) сущности представляют собой объекты, которые будут храниться в базе данных. Эти объекты маппируются на таблицы базы данных и могут содержать атрибуты, которые соответствуют столбцам таблицы.
1. Основные принципы сущности в JPA
Сущности JPA — это классы, которые автоматически маппируются на таблицы базы данных.
Важно понимать, что сущности:
Являются Java POJO (Plain Old Java Object) классами, которые должны иметь конструктор без параметров.
Должны быть аннотированы аннотацией @Entity.
Каждый объект сущности будет соответствовать одной строке в базе данных.
Для работы с сущностью в базе данных используется Entity Manager, который управляет состоянием объектов и их персистенцией.
2. Основные аннотации для сущностей
2.1 @Entity
Аннотация @Entity указывает, что класс является сущностью, которая будет маппироваться на таблицу в базе данных.
Класс, аннотированный @Entity, будет автоматически сопоставлен с таблицей в базе данных.
Если не указано, имя таблицы будет автоматически сформировано из имени класса.
2.2 @Table
Аннотация @Table используется для настройки таблицы, с которой будет работать сущность. Вы можете указать имя таблицы, схему и ограничения.
2.3 @Id
Аннотация @Id указывает на поле, которое является первичным ключом для сущности.
3. Стратегии генерации значений для первичного ключа
3.1 GenerationType.IDENTITY
Эта стратегия используется для автоматической генерации значений в столбце, обычно используется в базе данных с поддержкой автоинкремента.
3.2 GenerationType.SEQUENCE
Используется для генерации уникальных значений через последовательности базы данных.
3.3 GenerationType.TABLE
Использует специальную таблицу для генерации уникальных значений.
4. Маппинг полей сущности на столбцы таблицы
4.1 @Column
Аннотация @Column используется для маппинга поля сущности на столбец таблицы.
4.2 @Lob
Аннотация @Lob используется для хранения больших объектов (например, текста или бинарных данных). Это может быть полезно для хранения изображений, файлов или больших текстовых полей.
4.3 @Transient
Аннотация @Transient используется для исключения поля из маппинга. Это поле не будет сохраняться в базе данных.
#Java #Training #Spring #JPA_Entity
В Java Persistence API (JPA) сущности представляют собой объекты, которые будут храниться в базе данных. Эти объекты маппируются на таблицы базы данных и могут содержать атрибуты, которые соответствуют столбцам таблицы.
1. Основные принципы сущности в JPA
Сущности JPA — это классы, которые автоматически маппируются на таблицы базы данных.
Важно понимать, что сущности:
Являются Java POJO (Plain Old Java Object) классами, которые должны иметь конструктор без параметров.
Должны быть аннотированы аннотацией @Entity.
Каждый объект сущности будет соответствовать одной строке в базе данных.
Для работы с сущностью в базе данных используется Entity Manager, который управляет состоянием объектов и их персистенцией.
2. Основные аннотации для сущностей
2.1 @Entity
Аннотация @Entity указывает, что класс является сущностью, которая будет маппироваться на таблицу в базе данных.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// геттеры и сеттеры
}
Класс, аннотированный @Entity, будет автоматически сопоставлен с таблицей в базе данных.
Если не указано, имя таблицы будет автоматически сформировано из имени класса.
2.2 @Table
Аннотация @Table используется для настройки таблицы, с которой будет работать сущность. Вы можете указать имя таблицы, схему и ограничения.
@Entity
@Table(name = "users", schema = "public")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
}
Атрибуты name и schema позволяют указать, на какую таблицу и в какой схеме будет маппироваться класс.
2.3 @Id
Аннотация @Id указывает на поле, которое является первичным ключом для сущности.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Поле, помеченное @Id, будет использоваться как первичный ключ таблицы.
Аннотация @GeneratedValue указывает стратегию генерации значений первичного ключа.
3. Стратегии генерации значений для первичного ключа
3.1 GenerationType.IDENTITY
Эта стратегия используется для автоматической генерации значений в столбце, обычно используется в базе данных с поддержкой автоинкремента.
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
3.2 GenerationType.SEQUENCE
Используется для генерации уникальных значений через последовательности базы данных.
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
@SequenceGenerator(name = "user_seq", sequenceName = "user_sequence")
private Long id;
3.3 GenerationType.TABLE
Использует специальную таблицу для генерации уникальных значений.
@GeneratedValue(strategy = GenerationType.TABLE, generator = "id_gen")
@TableGenerator(name = "id_gen", table = "id_generator")
private Long id;
4. Маппинг полей сущности на столбцы таблицы
4.1 @Column
Аннотация @Column используется для маппинга поля сущности на столбец таблицы.
@Column(name = "username", nullable = false, unique = true, length = 50)
private String username;
name: имя столбца.
nullable: указывает, может ли столбец быть пустым.
unique: задает уникальность значения столбца.
length: максимальная длина строкового значения.
4.2 @Lob
Аннотация @Lob используется для хранения больших объектов (например, текста или бинарных данных). Это может быть полезно для хранения изображений, файлов или больших текстовых полей.
@Lob
private String description;
4.3 @Transient
Аннотация @Transient используется для исключения поля из маппинга. Это поле не будет сохраняться в базе данных.
@Transient
private String temporaryData;
#Java #Training #Spring #JPA_Entity
5. Работа с жизненным циклом сущности
5.1 @PrePersist, @PostPersist
Эти аннотации позволяют реализовывать методы, которые вызываются перед или после вставки сущности в базу данных.
#Java #Training #Spring #JPA_Entity
5.1 @PrePersist, @PostPersist
Эти аннотации позволяют реализовывать методы, которые вызываются перед или после вставки сущности в базу данных.
@Entity
public class Order {
@Id
private Long id;
@PrePersist
public void prePersist() {
System.out.println("Before persisting the Order");
}
@PostPersist
public void postPersist() {
System.out.println("After persisting the Order");
}
}
#Java #Training #Spring #JPA_Entity
Репозитории в Spring Data: интерфейс CrudRepository и JpaRepository
В экосистеме Spring Data важнейшую роль играют репозитории, которые позволяют работать с данными, не пиша сложного SQL-кода. Два ключевых интерфейса, которые используются чаще всего, — это CrudRepository и JpaRepository.
CrudRepository
CrudRepository — это базовый интерфейс для работы с CRUD-операциями (Create, Read, Update, Delete). Он предоставляет набор стандартных методов, которые можно использовать для управления сущностями.
Основные методы CrudRepository:
save(S entity): Сохраняет сущность.
findById(ID id): Находит сущность по ID.
existsById(ID id): Проверяет существование сущности с заданным ID.
findAll(): Возвращает все сущности.
deleteById(ID id): Удаляет сущность по ID.
deleteAll(): Удаляет все сущности.
Пример использования CrudRepository:
JpaRepository
JpaRepository расширяет возможности CrudRepository и добавляет дополнительные методы для работы с коллекциями и пагинацией. Этот интерфейс является частью Spring Data JPA и более мощным инструментом.
Основные методы JpaRepository:
saveAll(Iterable<S> entities): Сохраняет коллекцию сущностей.
findAll(Sort sort): Возвращает все сущности с сортировкой.
findAll(Pageable pageable): Возвращает сущности с постраничной разбивкой.
flush(): Принудительно записывает изменения в базу данных.
saveAndFlush(S entity): Сохраняет сущность и немедленно записывает изменения.
Пример использования JpaRepository:
Теперь можно использовать более сложные запросы, такие как сортировка и пагинация:
Основные отличия CrudRepository и JpaRepository:
JpaRepository предоставляет больше методов, что полезно для сложных операций.
CrudRepository минималистичен, подходит для простых операций CRUD.
JpaRepository работает на базе JPA, добавляя функциональность для сортировки и пагинации.
Использование JpaRepository предпочтительно, если проект использует JPA и предполагает расширенные функции.
Пример работы с JpaRepository
Предположим, у нас есть сущность Order:
Теперь создадим репозиторий:
Мы можем легко сохранять и получать данные:
#Java #Training #Spring #CrudRepository #JpaRepository
В экосистеме Spring Data важнейшую роль играют репозитории, которые позволяют работать с данными, не пиша сложного SQL-кода. Два ключевых интерфейса, которые используются чаще всего, — это CrudRepository и JpaRepository.
CrudRepository
CrudRepository — это базовый интерфейс для работы с CRUD-операциями (Create, Read, Update, Delete). Он предоставляет набор стандартных методов, которые можно использовать для управления сущностями.
Основные методы CrudRepository:
save(S entity): Сохраняет сущность.
findById(ID id): Находит сущность по ID.
existsById(ID id): Проверяет существование сущности с заданным ID.
findAll(): Возвращает все сущности.
deleteById(ID id): Удаляет сущность по ID.
deleteAll(): Удаляет все сущности.
Пример использования CrudRepository:
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Long> {
}
В этом примере UserRepository предоставляет все стандартные CRUD-операции для работы с сущностью User.
JpaRepository
JpaRepository расширяет возможности CrudRepository и добавляет дополнительные методы для работы с коллекциями и пагинацией. Этот интерфейс является частью Spring Data JPA и более мощным инструментом.
Основные методы JpaRepository:
saveAll(Iterable<S> entities): Сохраняет коллекцию сущностей.
findAll(Sort sort): Возвращает все сущности с сортировкой.
findAll(Pageable pageable): Возвращает сущности с постраничной разбивкой.
flush(): Принудительно записывает изменения в базу данных.
saveAndFlush(S entity): Сохраняет сущность и немедленно записывает изменения.
Пример использования JpaRepository:
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository extends JpaRepository<Product, Long> {
}
Теперь можно использовать более сложные запросы, такие как сортировка и пагинация:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public Page<Product> getProducts(int page, int size) {
return productRepository.findAll(PageRequest.of(page, size));
}
}
Основные отличия CrudRepository и JpaRepository:
JpaRepository предоставляет больше методов, что полезно для сложных операций.
CrudRepository минималистичен, подходит для простых операций CRUD.
JpaRepository работает на базе JPA, добавляя функциональность для сортировки и пагинации.
Использование JpaRepository предпочтительно, если проект использует JPA и предполагает расширенные функции.
Пример работы с JpaRepository
Предположим, у нас есть сущность Order:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
private String description;
private double price;
// getters and setters
}
Теперь создадим репозиторий:
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderRepository extends JpaRepository<Order, Long> {
}
Мы можем легко сохранять и получать данные:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public Order saveOrder(Order order) {
return orderRepository.save(order);
}
public List<Order> getAllOrders() {
return orderRepository.findAll();
}
}
#Java #Training #Spring #CrudRepository #JpaRepository
Продолжаем устранять пробелы в многопоточке Java. Сегодня рассмотрим интерфейс Callable.
Callable
Callable — это функциональный интерфейс в Java, который представляет задачу, выполняемую в отдельном потоке. Он был введен в Java 5 вместе с пакетами для многозадачности (java.util.concurrent), и его основное назначение — предоставить способ выполнения асинхронных операций, которые могут вернуть результат.
1. Основные особенности Callable
Интерфейс Callable аналогичен интерфейсу Runnable, но с двумя важными отличиями:
Возвращаемое значение: В отличие от Runnable, который не возвращает значения (void run()), метод call() интерфейса Callable может возвращать результат. Это позволяет возвращать какие-либо данные (например, результат вычислений или ошибку).
Исключения: Метод call() может бросать исключения, в отличие от метода run() интерфейса Runnable, который не может выбрасывать проверяемые исключения.
Возвращаемое значение: Метод call() возвращает объект типа V (генерик), который может быть любым типом. Это возвращаемое значение удобно для получения результатов выполнения асинхронных задач.
Использование с ExecutorService:
Задачи, реализующие Callable, часто выполняются через ExecutorService, который предоставляет методы для асинхронного выполнения таких задач.
2. Методы интерфейса Callable
call() — это основной метод интерфейса. Он выполняет задачу и возвращает результат типа V. Также этот метод может выбрасывать проверяемые исключения (Exception), что отличает его от метода run() интерфейса Runnable, который не может этого делать.
3. Основные классы и интерфейсы для работы с Callable
Чтобы эффективно использовать интерфейс Callable, в Java существует несколько классов и интерфейсов для работы с многозадачностью:
ExecutorService — интерфейс для управления пулом потоков и выполнения задач в асинхронном режиме.
Future<V> — интерфейс, который представляет результат асинхронной задачи. Он используется для получения результата выполнения Callable после завершения его работы. Через объект Future можно проверить статус выполнения задачи, отменить её или получить результат (если задача завершена).
Пример использования Callable и ExecutorService:
4. Методы интерфейса Future
Объект Future, который возвращается методом submit() объекта ExecutorService, предоставляет несколько полезных методов для работы с результатом асинхронной задачи:
get(): Блокирует текущий поток до получения результата выполнения задачи. Если задача завершена с ошибкой, этот метод выбрасывает ExecutionException.
get(long timeout, TimeUnit unit): Блокирует текущий поток до получения результата или до истечения времени ожидания.
cancel(boolean mayInterruptIfRunning): Отменяет задачу. Если задача не начала выполнение, она будет отменена. Если она уже выполняется, её можно прервать, если указан параметр mayInterruptIfRunning = true.
isCancelled(): Проверяет, была ли задача отменена.
isDone(): Проверяет, завершена ли задача (независимо от того, успешно ли она завершилась).
#Java #Training #Multithreading #Callable
Callable
Callable — это функциональный интерфейс в Java, который представляет задачу, выполняемую в отдельном потоке. Он был введен в Java 5 вместе с пакетами для многозадачности (java.util.concurrent), и его основное назначение — предоставить способ выполнения асинхронных операций, которые могут вернуть результат.
1. Основные особенности Callable
Интерфейс Callable аналогичен интерфейсу Runnable, но с двумя важными отличиями:
Возвращаемое значение: В отличие от Runnable, который не возвращает значения (void run()), метод call() интерфейса Callable может возвращать результат. Это позволяет возвращать какие-либо данные (например, результат вычислений или ошибку).
Исключения: Метод call() может бросать исключения, в отличие от метода run() интерфейса Runnable, который не может выбрасывать проверяемые исключения.
Возвращаемое значение: Метод call() возвращает объект типа V (генерик), который может быть любым типом. Это возвращаемое значение удобно для получения результатов выполнения асинхронных задач.
Использование с ExecutorService:
Задачи, реализующие Callable, часто выполняются через ExecutorService, который предоставляет методы для асинхронного выполнения таких задач.
2. Методы интерфейса Callable
call() — это основной метод интерфейса. Он выполняет задачу и возвращает результат типа V. Также этот метод может выбрасывать проверяемые исключения (Exception), что отличает его от метода run() интерфейса Runnable, который не может этого делать.
public interface Callable<V> {
V call() throws Exception;
}
3. Основные классы и интерфейсы для работы с Callable
Чтобы эффективно использовать интерфейс Callable, в Java существует несколько классов и интерфейсов для работы с многозадачностью:
ExecutorService — интерфейс для управления пулом потоков и выполнения задач в асинхронном режиме.
Future<V> — интерфейс, который представляет результат асинхронной задачи. Он используется для получения результата выполнения Callable после завершения его работы. Через объект Future можно проверить статус выполнения задачи, отменить её или получить результат (если задача завершена).
Пример использования Callable и ExecutorService:
import java.util.concurrent.*;
public class CallableExample {
public static void main(String[] args) {
// Создаем ExecutorService с пулом потоков
ExecutorService executor = Executors.newFixedThreadPool(2);
// Задача, которая возвращает результат
Callable<Integer> task = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// Пример вычислений
return 123;
}
};
// Выполняем задачу и получаем объект Future
Future<Integer> future = executor.submit(task);
try {
// Получаем результат выполнения задачи
Integer result = future.get(); // блокирует текущий поток до получения результата
System.out.println("Результат: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
4. Методы интерфейса Future
Объект Future, который возвращается методом submit() объекта ExecutorService, предоставляет несколько полезных методов для работы с результатом асинхронной задачи:
get(): Блокирует текущий поток до получения результата выполнения задачи. Если задача завершена с ошибкой, этот метод выбрасывает ExecutionException.
get(long timeout, TimeUnit unit): Блокирует текущий поток до получения результата или до истечения времени ожидания.
cancel(boolean mayInterruptIfRunning): Отменяет задачу. Если задача не начала выполнение, она будет отменена. Если она уже выполняется, её можно прервать, если указан параметр mayInterruptIfRunning = true.
isCancelled(): Проверяет, была ли задача отменена.
isDone(): Проверяет, завершена ли задача (независимо от того, успешно ли она завершилась).
#Java #Training #Multithreading #Callable
5. Преимущества использования Callable
Возврат результата: В отличие от Runnable, который не возвращает результата, Callable может возвращать значения, что делает его полезным для вычислений, которые должны вернуть результат.
Обработка исключений: Callable позволяет методам выбрасывать проверяемые исключения, что полезно для обработки ошибок в многозадачных приложениях.
Параллельное выполнение: Использование ExecutorService с задачами типа Callable позволяет эффективно управлять пулом потоков, распределяя задачи между потоками.
6. Нюансы использования Callable
Блокировка с get(): Важно помнить, что метод get() блокирует текущий поток до тех пор, пока задача не завершится. Если вы вызываете get() на множестве задач, это может привести к значительным задержкам, если задачи не завершены вовремя.
Исключения в call(): Поскольку метод call() может выбрасывать исключения, важно правильно их обрабатывать в блоке try-catch, особенно если задача выполняет долгосрочную или ресурсозатратную операцию.
Параллельность: Если задач несколько, ExecutorService с пулом потоков позволяет выполнять их параллельно, но стоит быть осторожным с использованием ресурсов, так как слишком много потоков может привести к перегрузке системы.
Атомарность операций: Если задачи выполняют изменения общих данных, важно учитывать синхронизацию, чтобы избежать конфликтов между потоками.
Использование с Callable и Future для делегирования работы: Параллельное выполнение задач через ExecutorService и использование объектов Future позволяет строить более сложные асинхронные системы с возможностью отмены задач, получения результатов и обработки ошибок.
7. Пример использования Callable с возвращаемыми результатами и исключениями
#Java #Training #Medium #Multithreading #Callable
Возврат результата: В отличие от Runnable, который не возвращает результата, Callable может возвращать значения, что делает его полезным для вычислений, которые должны вернуть результат.
Обработка исключений: Callable позволяет методам выбрасывать проверяемые исключения, что полезно для обработки ошибок в многозадачных приложениях.
Параллельное выполнение: Использование ExecutorService с задачами типа Callable позволяет эффективно управлять пулом потоков, распределяя задачи между потоками.
6. Нюансы использования Callable
Блокировка с get(): Важно помнить, что метод get() блокирует текущий поток до тех пор, пока задача не завершится. Если вы вызываете get() на множестве задач, это может привести к значительным задержкам, если задачи не завершены вовремя.
Исключения в call(): Поскольку метод call() может выбрасывать исключения, важно правильно их обрабатывать в блоке try-catch, особенно если задача выполняет долгосрочную или ресурсозатратную операцию.
Параллельность: Если задач несколько, ExecutorService с пулом потоков позволяет выполнять их параллельно, но стоит быть осторожным с использованием ресурсов, так как слишком много потоков может привести к перегрузке системы.
Атомарность операций: Если задачи выполняют изменения общих данных, важно учитывать синхронизацию, чтобы избежать конфликтов между потоками.
Использование с Callable и Future для делегирования работы: Параллельное выполнение задач через ExecutorService и использование объектов Future позволяет строить более сложные асинхронные системы с возможностью отмены задач, получения результатов и обработки ошибок.
7. Пример использования Callable с возвращаемыми результатами и исключениями
import java.util.concurrent.*;
public class CallableWithException {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<String> task = () -> {
// Исключение для демонстрации обработки ошибок
if (true) {
throw new IllegalArgumentException("Произошла ошибка");
}
return "Успешно выполнено!";
};
Future<String> future = executor.submit(task);
try {
String result = future.get(); // Это вызовет исключение, если задача выбросит ошибку
System.out.println("Результат: " + result);
} catch (ExecutionException e) {
System.out.println("Ошибка выполнения задачи: " + e.getCause());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
executor.shutdown();
}
}
}
#Java #Training #Medium #Multithreading #Callable
Что выведет код?
#Tasks
import java.util.*;
public class Task211124_1 {
public static void main(String[] args) {
List<String> items = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J");
int pageSize = 3;
int page = 3;
List<String> pageItems = paginate(items, page, pageSize);
System.out.println(pageItems);
}
public static List<String> paginate(List<String> items, int page, int pageSize) {
int start = (page - 1) * pageSize;
int end = Math.min(start + pageSize, items.size());
return items.subList(start, end);
}
}
#Tasks
Создание методов для поиска данных в Spring Data JPA
Spring Data JPA позволяет разработчикам создавать методы для поиска данных без написания SQL-запросов. Это достигается благодаря методам запроса на основе имен и JPQL-запросам.
Методы поиска по имени
Spring Data позволяет создавать методы поиска, просто добавляя определенные суффиксы в их имена.
Примеры методов поиска:
findBy — находит по полю.
findBy...And... — находит по нескольким полям.
findBy...Or... — находит по одному из нескольких полей.
Предположим, у нас есть сущность User:
Теперь создадим репозиторий:
Использование этих методов:
Методы с использованием JPQL
JPQL (Java Persistence Query Language) — это язык запросов, похожий на SQL, но использующий сущности вместо таблиц. В Spring Data JPA вы можете использовать JPQL-запросы с аннотацией @Query.
Добавим метод с JPQL-запросом в UserRepository:
Теперь мы можем использовать эти методы для более гибкого поиска:
Сортировка и пагинация
Spring Data JPA позволяет легко добавлять сортировку и пагинацию к любым методам поиска.
Пример с использованием сортировки:
Пример с пагинацией:
#Java #Training #Spring #JPQL
Spring Data JPA позволяет разработчикам создавать методы для поиска данных без написания SQL-запросов. Это достигается благодаря методам запроса на основе имен и JPQL-запросам.
Методы поиска по имени
Spring Data позволяет создавать методы поиска, просто добавляя определенные суффиксы в их имена.
Примеры методов поиска:
findBy — находит по полю.
findBy...And... — находит по нескольким полям.
findBy...Or... — находит по одному из нескольких полей.
Предположим, у нас есть сущность User:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private String email;
// getters and setters
}
Теперь создадим репозиторий:
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByName(String name);
User findByEmail(String email);
List<User> findByNameAndEmail(String name, String email);
}
Использование этих методов:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> getUsersByName(String name) {
return userRepository.findByName(name);
}
public User getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
public List<User> getUsersByNameAndEmail(String name, String email) {
return userRepository.findByNameAndEmail(name, email);
}
}
Методы с использованием JPQL
JPQL (Java Persistence Query Language) — это язык запросов, похожий на SQL, но использующий сущности вместо таблиц. В Spring Data JPA вы можете использовать JPQL-запросы с аннотацией @Query.
Добавим метод с JPQL-запросом в UserRepository:
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.name = :name")
List<User> findUsersByName(@Param("name") String name);
@Query("SELECT u FROM User u WHERE u.email LIKE %:emailDomain")
List<User> findUsersByEmailDomain(@Param("emailDomain") String emailDomain);
}
Теперь мы можем использовать эти методы для более гибкого поиска:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> findUsersByEmailDomain(String domain) {
return userRepository.findUsersByEmailDomain(domain);
}
}
Сортировка и пагинация
Spring Data JPA позволяет легко добавлять сортировку и пагинацию к любым методам поиска.
Пример с использованием сортировки:
import org.springframework.data.domain.Sort;
List<User> findAll(Sort sort);
List<User> usersSortedByName = userRepository.findAll(Sort.by("name"));
Пример с пагинацией:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
Page<User> findAll(PageRequest pageable);
Page<User> usersPage = userRepository.findAll(PageRequest.of(0, 10));
#Java #Training #Spring #JPQL
Что выведет код?
Задача по Spring JDBC, JPQL, @JpaRepository, @Entity, @Table, @Id создание и выполнение CRUD операций в Spring. Сложность легкая.
Подробный разбор через 30 минут!🫡
#TasksSpring
Задача по Spring JDBC, JPQL, @JpaRepository, @Entity, @Table, @Id создание и выполнение CRUD операций в Spring. Сложность легкая.
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.List;
@SpringBootApplication
public class Main211124_2 {
public static void main(String[] args) {
SpringApplication.run(Main211124_2.class, args);
}
@Bean
public CommandLineRunner demo(UserRepository2111 repository) {
return args -> {
repository.save(new User2111("Alice", 30));
repository.save(new User2111("Bob", 25));
List<User2111> users = repository.findByAgeGreaterThan(20);
users.forEach(user -> System.out.println(user.getName()));
};
}
}
@Entity
@Table(name = "users2111")
class User2111 {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int age;
// Конструкторы, геттеры и сеттеры
public User2111() {}
public User2111(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
}
@Repository
interface UserRepository2111 extends JpaRepository<User2111, Long> {
List<User2111> findByAgeGreaterThan(int age);
}
#TasksSpring
Подробный разбор решения задачи Task211124_2
1. Контекст задачи:
Задача демонстрирует использование Spring Data JPA для работы с базой данных. В частности, она показывает, как использовать аннотации JPA (@Entity, @Table, @Id) для определения сущностей, а также JpaRepository для выполнения CRUD операций и запросов с использованием JPQL. Основное внимание уделяется выполнению операций сохранения и выборки данных.
2. Ключевые элементы кода:
Аннотация @SpringBootApplication:
Указывает, что класс Main211124_2 является главным классом Spring Boot приложения. Она включает в себя @Configuration, @EnableAutoConfiguration, и @ComponentScan, что автоматически настраивает компоненты Spring, включая Spring Data JPA.
Аннотация @Entity:
Класс User2111 помечен аннотацией @Entity, что указывает JPA, что этот класс соответствует таблице в базе данных. Он представляет собой сущность, с которой будет работать JPA.
Аннотация @Table(name = "users2111"):
Определяет, что эта сущность будет сопоставлена с таблицей users2111 в базе данных. Если аннотация @Table не указана, JPA по умолчанию использует имя класса как имя таблицы.
Аннотация @Id и @GeneratedValue:
Поле id помечено как первичный ключ с помощью аннотации @Id.
Аннотация @GeneratedValue(strategy = GenerationType.AUTO) указывает, что значение для поля id будет генерироваться автоматически (обычно с использованием автоинкремента в базе данных).
Интерфейс UserRepository2111 и аннотация @Repository:
UserRepository2111 расширяет JpaRepository, что предоставляет стандартные методы CRUD (например, save, findAll, deleteById).
Метод findByAgeGreaterThan(int age) позволяет выполнять JPQL-запрос для получения всех пользователей, у которых возраст больше заданного.
Использование CommandLineRunner:
CommandLineRunner используется для выполнения кода сразу после запуска приложения. В данном случае он выполняет следующие шаги:
Сохраняет в базу данных двух пользователей: "Alice" с возрастом 30 и "Bob" с возрастом 25.
Выполняет запрос findByAgeGreaterThan(20), возвращающий всех пользователей с возрастом больше 20.
Выводит имена выбранных пользователей в консоль.
3. Сценарий работы программы:
Запуск приложения:
Программа запускается с помощью SpringApplication.run(Main211124_2.class, args);. Spring Boot настраивает подключение к базе данных и компоненты Spring Data JPA.
Вставка данных:
Метод save() из JpaRepository используется для сохранения двух записей:
User2111("Alice", 30)
User2111("Bob", 25)
Выполнение запроса:
Метод findByAgeGreaterThan(20) выбирает всех пользователей, у которых возраст больше 20. Это JPQL-запрос, автоматически формируемый Spring Data JPA на основе имени метода.
Вывод данных:
Полученные записи проходят через метод forEach, который вызывает System.out.println для вывода имен в консоль.
4. Ключевые моменты и выводы:
Использование JPA и Spring Data:
Spring Data JPA значительно упрощает работу с базой данных, предоставляя готовые методы для выполнения CRUD операций и поддерживая создание запросов на основе имен методов.
Аннотации JPA:
@Entity и @Table используются для связывания класса с таблицей в базе данных.
@Id и @GeneratedValue обеспечивают автоматическую генерацию уникальных идентификаторов для каждой записи.
JPQL-запросы и JpaRepository:
Метод findByAgeGreaterThan демонстрирует, как можно выполнять запросы к базе данных, просто определяя метод с соответствующим именем в репозитории.
Гибкость CommandLineRunner:
Использование CommandLineRunner позволяет запускать код после загрузки контекста Spring, что удобно для тестирования операций с базой данных.
#Solution_TasksSpring
1. Контекст задачи:
Задача демонстрирует использование Spring Data JPA для работы с базой данных. В частности, она показывает, как использовать аннотации JPA (@Entity, @Table, @Id) для определения сущностей, а также JpaRepository для выполнения CRUD операций и запросов с использованием JPQL. Основное внимание уделяется выполнению операций сохранения и выборки данных.
2. Ключевые элементы кода:
Аннотация @SpringBootApplication:
Указывает, что класс Main211124_2 является главным классом Spring Boot приложения. Она включает в себя @Configuration, @EnableAutoConfiguration, и @ComponentScan, что автоматически настраивает компоненты Spring, включая Spring Data JPA.
Аннотация @Entity:
Класс User2111 помечен аннотацией @Entity, что указывает JPA, что этот класс соответствует таблице в базе данных. Он представляет собой сущность, с которой будет работать JPA.
Аннотация @Table(name = "users2111"):
Определяет, что эта сущность будет сопоставлена с таблицей users2111 в базе данных. Если аннотация @Table не указана, JPA по умолчанию использует имя класса как имя таблицы.
Аннотация @Id и @GeneratedValue:
Поле id помечено как первичный ключ с помощью аннотации @Id.
Аннотация @GeneratedValue(strategy = GenerationType.AUTO) указывает, что значение для поля id будет генерироваться автоматически (обычно с использованием автоинкремента в базе данных).
Интерфейс UserRepository2111 и аннотация @Repository:
UserRepository2111 расширяет JpaRepository, что предоставляет стандартные методы CRUD (например, save, findAll, deleteById).
Метод findByAgeGreaterThan(int age) позволяет выполнять JPQL-запрос для получения всех пользователей, у которых возраст больше заданного.
Использование CommandLineRunner:
CommandLineRunner используется для выполнения кода сразу после запуска приложения. В данном случае он выполняет следующие шаги:
Сохраняет в базу данных двух пользователей: "Alice" с возрастом 30 и "Bob" с возрастом 25.
Выполняет запрос findByAgeGreaterThan(20), возвращающий всех пользователей с возрастом больше 20.
Выводит имена выбранных пользователей в консоль.
3. Сценарий работы программы:
Запуск приложения:
Программа запускается с помощью SpringApplication.run(Main211124_2.class, args);. Spring Boot настраивает подключение к базе данных и компоненты Spring Data JPA.
Вставка данных:
Метод save() из JpaRepository используется для сохранения двух записей:
User2111("Alice", 30)
User2111("Bob", 25)
Выполнение запроса:
Метод findByAgeGreaterThan(20) выбирает всех пользователей, у которых возраст больше 20. Это JPQL-запрос, автоматически формируемый Spring Data JPA на основе имени метода.
Вывод данных:
Полученные записи проходят через метод forEach, который вызывает System.out.println для вывода имен в консоль.
4. Ключевые моменты и выводы:
Использование JPA и Spring Data:
Spring Data JPA значительно упрощает работу с базой данных, предоставляя готовые методы для выполнения CRUD операций и поддерживая создание запросов на основе имен методов.
Аннотации JPA:
@Entity и @Table используются для связывания класса с таблицей в базе данных.
@Id и @GeneratedValue обеспечивают автоматическую генерацию уникальных идентификаторов для каждой записи.
JPQL-запросы и JpaRepository:
Метод findByAgeGreaterThan демонстрирует, как можно выполнять запросы к базе данных, просто определяя метод с соответствующим именем в репозитории.
Гибкость CommandLineRunner:
Использование CommandLineRunner позволяет запускать код после загрузки контекста Spring, что удобно для тестирования операций с базой данных.
#Solution_TasksSpring
Транзакции в Spring
В современных приложениях, особенно тех, что работают с базами данных, транзакции играют ключевую роль. Они обеспечивают выполнение операций в базе данных с гарантией целостности данных и устойчивости к сбоям.
Транзакция — это последовательность операций, которые выполняются как единое целое. Если хотя бы одна из операций в транзакции завершается с ошибкой, все изменения, сделанные в рамках этой транзакции, откатываются.
Ключевые свойства транзакций определяются через ACID:
Atomicity (Атомарность): Все операции в транзакции выполняются или ни одна из них.
Consistency (Согласованность): Транзакция переводит базу данных из одного согласованного состояния в другое.
Isolation (Изолированность): Операции в транзакции не видимы другим транзакциям, пока они не завершатся.
Durability (Устойчивость): После успешного завершения транзакции изменения остаются сохраненными в базе данных, даже в случае сбоя системы.
Пример транзакции в SQL
Предположим, у нас есть две таблицы: accounts и transactions. Нам нужно перевести деньги с одного счета на другой.
Транзакции в Spring
Spring обеспечивает управление транзакциями с помощью своих инструментов.
В экосистеме Spring можно использовать различные подходы:
Программное управление транзакциями — требуется вручную управлять началом и завершением транзакций.
Декларативное управление транзакциями — управление транзакциями осуществляется автоматически с помощью аннотаций или конфигураций. Этот подход является наиболее популярным, так как упрощает код.
Программное управление транзакциями
Для ручного управления транзакциями в Spring используется PlatformTransactionManager.
Пример:
Декларативное управление транзакциями
Более простой и популярный способ — это использование аннотации @Transactional.
Преимущества транзакций:
Целостность данных: Транзакции гарантируют, что данные останутся в корректном состоянии.
Стабильность системы: Операции либо полностью завершаются, либо полностью откатываются.
Упрощение отладки: Вы можете быть уверены, что в случае сбоя данные не будут повреждены.
#Java #Training #Spring #Transactions #ACID
В современных приложениях, особенно тех, что работают с базами данных, транзакции играют ключевую роль. Они обеспечивают выполнение операций в базе данных с гарантией целостности данных и устойчивости к сбоям.
Транзакция — это последовательность операций, которые выполняются как единое целое. Если хотя бы одна из операций в транзакции завершается с ошибкой, все изменения, сделанные в рамках этой транзакции, откатываются.
Ключевые свойства транзакций определяются через ACID:
Atomicity (Атомарность): Все операции в транзакции выполняются или ни одна из них.
Consistency (Согласованность): Транзакция переводит базу данных из одного согласованного состояния в другое.
Isolation (Изолированность): Операции в транзакции не видимы другим транзакциям, пока они не завершатся.
Durability (Устойчивость): После успешного завершения транзакции изменения остаются сохраненными в базе данных, даже в случае сбоя системы.
Пример транзакции в SQL
Предположим, у нас есть две таблицы: accounts и transactions. Нам нужно перевести деньги с одного счета на другой.
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
INSERT INTO transactions (from_account, to_account, amount) VALUES (1, 2, 100);
COMMIT;
Если любая из этих операций не выполняется, все изменения откатываются.
Транзакции в Spring
Spring обеспечивает управление транзакциями с помощью своих инструментов.
В экосистеме Spring можно использовать различные подходы:
Программное управление транзакциями — требуется вручную управлять началом и завершением транзакций.
Декларативное управление транзакциями — управление транзакциями осуществляется автоматически с помощью аннотаций или конфигураций. Этот подход является наиболее популярным, так как упрощает код.
Программное управление транзакциями
Для ручного управления транзакциями в Spring используется PlatformTransactionManager.
Пример:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Service
public class TransactionalService {
@Autowired
private PlatformTransactionManager transactionManager;
public void performTransactionalOperation() {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// Ваши операции с базой данных
performDatabaseOperation1();
performDatabaseOperation2();
// Подтверждение транзакции
transactionManager.commit(status);
} catch (Exception e) {
// Откат транзакции в случае ошибки
transactionManager.rollback(status);
throw e;
}
}
private void performDatabaseOperation1() {
// Код операции 1
}
private void performDatabaseOperation2() {
// Код операции 2
}
}
Этот метод предоставляет полный контроль над транзакциями, но делает код более сложным.
Декларативное управление транзакциями
Более простой и популярный способ — это использование аннотации @Transactional.
Преимущества транзакций:
Целостность данных: Транзакции гарантируют, что данные останутся в корректном состоянии.
Стабильность системы: Операции либо полностью завершаются, либо полностью откатываются.
Упрощение отладки: Вы можете быть уверены, что в случае сбоя данные не будут повреждены.
#Java #Training #Spring #Transactions #ACID
Продолжаем устранять пробелы в многопоточке Java. Сегодня рассмотрим Semaphore и CountDownLatch.
Semaphore
Semaphore используется для управления доступом к ограниченному ресурсу, который может одновременно использоваться только определенным числом потоков. Это полезно для реализации пула ресурсов, например, подключения к базе данных, файловых дескрипторов и других ограниченных ресурсов.
Основные методы Semaphore
acquire(): Захватывает разрешение (пермит). Если разрешений нет, поток блокируется до тех пор, пока одно из них не станет доступным.
acquire(int permits): Захватывает указанное количество разрешений. Если их недостаточно, поток блокируется.
release(): Освобождает разрешение, увеличивая количество доступных разрешений на 1.
release(int permits): Освобождает указанное количество разрешений.
availablePermits(): Возвращает количество доступных разрешений.
tryAcquire(): Пытается захватить разрешение. Возвращает true, если удалось получить разрешение, иначе false.
tryAcquire(int permits, long timeout, TimeUnit unit): Пытается получить разрешения с указанным временем ожидания.
isFair(): Проверяет, использует ли семафор справедливый порядок захвата разрешений (FIFO).
Типы Semaphore
Несправедливый (non-fair): Потоки получают доступ в произвольном порядке (по умолчанию).
Справедливый (fair): Потоки получают доступ в порядке очереди (FIFO).
Пример использования Semaphore
Результат:
В каждый момент времени только 2 потока выполняют свою работу.
Остальные ждут освобождения разрешения.
Нюансы Semaphore
Справедливость (fair vs non-fair):
Несправедливый семафор быстрее, но поток может быть отложен, даже если он ожидает дольше других.
Справедливый семафор обеспечивает порядок очереди, но имеет больше накладных расходов.
Deadlock (взаимная блокировка):
Если поток забывает вызвать release() после acquire(), это приведет к "утечке" разрешений.
Пул ресурсов:
Часто используется для ограничения числа потоков, работающих с одним и тем же ресурсом.
#Java #Training #Multithreading #Semaphore
Semaphore
Semaphore используется для управления доступом к ограниченному ресурсу, который может одновременно использоваться только определенным числом потоков. Это полезно для реализации пула ресурсов, например, подключения к базе данных, файловых дескрипторов и других ограниченных ресурсов.
Основные методы Semaphore
acquire(): Захватывает разрешение (пермит). Если разрешений нет, поток блокируется до тех пор, пока одно из них не станет доступным.
acquire(int permits): Захватывает указанное количество разрешений. Если их недостаточно, поток блокируется.
release(): Освобождает разрешение, увеличивая количество доступных разрешений на 1.
release(int permits): Освобождает указанное количество разрешений.
availablePermits(): Возвращает количество доступных разрешений.
tryAcquire(): Пытается захватить разрешение. Возвращает true, если удалось получить разрешение, иначе false.
tryAcquire(int permits, long timeout, TimeUnit unit): Пытается получить разрешения с указанным временем ожидания.
isFair(): Проверяет, использует ли семафор справедливый порядок захвата разрешений (FIFO).
Типы Semaphore
Несправедливый (non-fair): Потоки получают доступ в произвольном порядке (по умолчанию).
Справедливый (fair): Потоки получают доступ в порядке очереди (FIFO).
Пример использования Semaphore
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
// Семафор с 2 разрешениями
Semaphore semaphore = new Semaphore(2);
// Пул из 5 потоков
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int threadId = i;
executor.submit(() -> {
try {
System.out.println("Thread " + threadId + " is waiting for permit...");
semaphore.acquire(); // Получаем разрешение
System.out.println("Thread " + threadId + " acquired permit.");
Thread.sleep(2000); // Имитируем использование ресурса
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
System.out.println("Thread " + threadId + " released permit.");
semaphore.release(); // Освобождаем разрешение
}
});
}
executor.shutdown();
}
}
Результат:
В каждый момент времени только 2 потока выполняют свою работу.
Остальные ждут освобождения разрешения.
Нюансы Semaphore
Справедливость (fair vs non-fair):
Несправедливый семафор быстрее, но поток может быть отложен, даже если он ожидает дольше других.
Справедливый семафор обеспечивает порядок очереди, но имеет больше накладных расходов.
Deadlock (взаимная блокировка):
Если поток забывает вызвать release() после acquire(), это приведет к "утечке" разрешений.
Пул ресурсов:
Часто используется для ограничения числа потоков, работающих с одним и тем же ресурсом.
#Java #Training #Multithreading #Semaphore
CountDownLatch
CountDownLatch используется для обеспечения синхронизации между потоками, позволяя одному или нескольким потокам ждать завершения операций в других потоках.
Основные методы CountDownLatch
await(): Блокирует поток до тех пор, пока счетчик не станет равен 0.
countDown(): Уменьшает значение счетчика на 1.
getCount(): Возвращает текущее значение счетчика.
await(long timeout, TimeUnit unit): Ждет заданное время. Если счетчик не достигает 0 за указанное время, поток продолжит выполнение.
Пример использования CountDownLatch
Результат:
Основной поток будет ждать завершения всех 3 задач.
После выполнения всех потоков (latch.countDown() вызывается 3 раза), основной поток продолжит выполнение.
Нюансы CountDownLatch
Одноразовый:
CountDownLatch нельзя сбросить или переиспользовать. Если нужно использовать его несколько раз, рассмотрите использование CyclicBarrier.
Потокобезопасность:
Все методы потокобезопасны и могут использоваться несколькими потоками одновременно.
Применение:
Инициализация или подготовка перед началом основной работы.
Ожидание завершения группы задач.
Когда использовать Semaphore или CountDownLatch?
Используйте Semaphore, если нужно управлять доступом к ограниченным ресурсам (например, пул соединений).
Используйте CountDownLatch, если потоки должны дождаться выполнения определенного количества задач, прежде чем продолжить выполнение.
Реальные примеры
Semaphore: Ограничение количества одновременных соединений
CountDownLatch: Ожидание завершения загрузки данных
#Java #Training #Multithreading #CountDownLatch
CountDownLatch используется для обеспечения синхронизации между потоками, позволяя одному или нескольким потокам ждать завершения операций в других потоках.
Основные методы CountDownLatch
await(): Блокирует поток до тех пор, пока счетчик не станет равен 0.
countDown(): Уменьшает значение счетчика на 1.
getCount(): Возвращает текущее значение счетчика.
await(long timeout, TimeUnit unit): Ждет заданное время. Если счетчик не достигает 0 за указанное время, поток продолжит выполнение.
Пример использования CountDownLatch
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
// Создаем CountDownLatch с начальным значением 3
CountDownLatch latch = new CountDownLatch(3);
// Потоки, выполняющие задачи
Runnable task = () -> {
System.out.println(Thread.currentThread().getName() + " is working...");
try {
Thread.sleep(1000); // Имитация работы
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " finished.");
latch.countDown(); // Уменьшаем значение счетчика
};
// Запускаем 3 потока
for (int i = 0; i < 3; i++) {
new Thread(task).start();
}
System.out.println("Main thread is waiting for tasks to finish...");
latch.await(); // Ждем, пока счетчик не станет равен 0
System.out.println("All tasks are finished. Main thread resumes.");
}
}
Результат:
Основной поток будет ждать завершения всех 3 задач.
После выполнения всех потоков (latch.countDown() вызывается 3 раза), основной поток продолжит выполнение.
Нюансы CountDownLatch
Одноразовый:
CountDownLatch нельзя сбросить или переиспользовать. Если нужно использовать его несколько раз, рассмотрите использование CyclicBarrier.
Потокобезопасность:
Все методы потокобезопасны и могут использоваться несколькими потоками одновременно.
Применение:
Инициализация или подготовка перед началом основной работы.
Ожидание завершения группы задач.
Когда использовать Semaphore или CountDownLatch?
Используйте Semaphore, если нужно управлять доступом к ограниченным ресурсам (например, пул соединений).
Используйте CountDownLatch, если потоки должны дождаться выполнения определенного количества задач, прежде чем продолжить выполнение.
Реальные примеры
Semaphore: Ограничение количества одновременных соединений
Semaphore semaphore = new Semaphore(10); // Максимум 10 соединений
// Каждый поток пытается установить соединение
Runnable connectTask = () -> {
try {
semaphore.acquire();
System.out.println("Connection established by " + Thread.currentThread().getName());
Thread.sleep(2000); // Используем соединение
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
System.out.println("Connection released by " + Thread.currentThread().getName());
semaphore.release();
}
};
CountDownLatch: Ожидание завершения загрузки данных
CountDownLatch latch = new CountDownLatch(3);
Runnable loadData = () -> {
try {
System.out.println(Thread.currentThread().getName() + " loading data...");
Thread.sleep(1000);
latch.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
new Thread(loadData).start();
new Thread(loadData).start();
new Thread(loadData).start();
latch.await();
System.out.println("All data loaded. Proceeding to next step.");
#Java #Training #Multithreading #CountDownLatch