技術共有

スレッド プール操作データベースにスレッド セーフティの問題があります

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

目次

1 はじめに

2. 質問

3. 解決策

3.1. 方法 1: データベースの制約

3.2. 方法 2: ロックを使用してスレッドを制限する

4. まとめ


1 はじめに

現在の要件は、データを処理し、データをデータベースに保存することです。保存プロセスでは、まず、データがデータベースに保存されているかどうかがクエリされます。処理するデータが大きすぎるため、スレッド プールが必要になります。同時処理に使用されます。

2. 質問

上記の方法を使用してロジックを作成すると、スレッド セーフティの問題が発生します。つまり、チェックしながら挿入するため (データベース クエリ データの不整合の問題)、結果としてデータの挿入が繰り返されることになります。したがって、データの重複の問題を制限する手段を採用する必要があります。

3. 解決策

3.1. 方法 1: データベースの制約

データベース内のフィールドを一意のインデックスとして設計します。クエリ中に対応するデータ レコードが見つからない場合でも (実際にはデータベースにすでに存在します)、挿入中に重複フィールド エラー例外が報告され、データ レコードの挿入が失敗します。重複排除の問題を達成するため。

3.2. 方法 2: ロックを使用してスレッドを制限する

実際のビジネス シナリオに基づいて、適切なロック戦略を選択してください。楽観的ロックは書き込み競合が少ないシナリオに適しており、悲観的ロックは書き込み競合が多いシナリオに適しています。

スレッド同期: 次のような同期メカニズムを使用します。ReentrantLocksynchronizedなど、1 つのスレッドだけがクエリと挿入操作を同時に実行できるようにします。以下では、スレッド同期メカニズムとコード表示設計を使用しています。

mybatisインターフェースコード:

  1. import org.apache.ibatis.annotations.*;
  2. public interface UserMapper {
  3. @Select("SELECT COUNT(*) FROM user WHERE username = #{username}")
  4. int countByUsername(@Param("username") String username);
  5. @Insert("INSERT INTO user (username, password, email) VALUES (#{username}, #{password}, #{email})")
  6. @Options(useGeneratedKeys = true, keyProperty = "id")
  7. int insertUser(User user);
  8. }

サービス クラス コードを作成します。

  1. import org.apache.ibatis.session.SqlSession;
  2. import org.apache.ibatis.session.SqlSessionFactory;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. public class UserService {
  6. private final ExecutorService executorService;
  7. private final SqlSessionFactory sqlSessionFactory;
  8. public UserService(SqlSessionFactory sqlSessionFactory) {
  9. this.sqlSessionFactory = sqlSessionFactory;
  10. // 创建固定大小的线程池
  11. this.executorService = Executors.newFixedThreadPool(10);
  12. }
  13. public void addUser(User user) {
  14. executorService.submit(() -> {
  15. try (SqlSession session = sqlSessionFactory.openSession()) {
  16. UserMapper userMapper = session.getMapper(UserMapper.class);
  17. synchronized (UserService.class) {
  18. // 在同步块内执行查询和插入操作
  19. if (userMapper.countByUsername(user.getUsername()) == 0) {
  20. userMapper.insertUser(user);
  21. session.commit(); // 提交事务
  22. }
  23. }
  24. }
  25. });
  26. }
  27. }

上記はほんの小さなケースであり、実際の使用シナリオはニーズによって異なります。上記のコードを説明します。UserService.class 1 つのスレッドだけがクエリ操作と挿入操作を同時に実行できるようにするためのロック オブジェクトとして。これにより同時実行性の問題は回避されますが、特に同時実行性が高いシナリオではパフォーマンスのボトルネックが発生する可能性があります。パフォーマンスを最適化するには、よりきめ細かいロックまたはデータベース レベルのロック (悲観的ロックなど) の使用を検討できます。

4. まとめ

スレッド プール操作データベースのロジックは要件に従って設計されていますが、プロセス全体でスレッドの安全性の問題が発生するため、次の 2 つの解決策を簡単に紹介します: ① データベース テーブルに一意のインデックス フィールドを設定する ② ロックを使用してスレッドの同期を確保する。使用する場合は、実際のシナリオに合わせて調整してください。このプロセスは 2 つの方法のみが紹介されているため、詳しく理解するにはさらに勉強する必要があります。もちろん、私のちょっとしたヒントを通じて、あなたの混乱に少しでも答えられれば幸いです。


勉強すると寝たくなるのは、そこから夢が始まるからです。
ଘ(੭ˊᵕˋ)੭ (幸せ) ଘ(੭ˊᵕˋ)੭ (幸せ) ଘ(੭ˊᵕˋ)੭ (幸せ) ଘ(੭ˊᵕˋ)੭ (幸せ) ଘ(੭ˊᵕˋ)੭ (幸せ)
------コードを書かないと目立たないシャオ・リウ