華為杭研所(面接内容)
マルチスレッド
-
スレッドを作成する複数の方法
- Threadクラスを継承し、runメソッドをオーバーライドする
- Runnableインターフェースを実装し、runメソッドをオーバーライドする。その後、Threadオブジェクトを作成し、Runnableタスクを渡す
-
Callableインターフェースを実装し、callメソッドをオーバーライドする。FutureTaskを作成して実行結果を取得する
public class CallableTest { public static void main(String[] args) throws Exception { MyThread task = new MyThread(); FutureTask<Integer> result = new FutureTask<>(task); new Thread(result).start(); // 結果の取得は同期的に行われるため、callメソッド完了後にのみ取得可能 Integer sum = result.get(); System.out.println(sum); } } class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } return sum; } }- スレッドプールを使用してスレッドを作成する(スレッドプールとCallableの組み合わせ、submitでタスクを送信)
-
サードパーティのAPIを呼び出す際、1秒間に10回しか許可されない場合の処理方法
-
シングルスレッド環境の場合(マルチスレッドを考慮する必要なし)
- リクエスト回数と時間をカウントする。リクエストごとにカウントを+1する。1秒経過したらThread.sleep(1)を実行する。また、リクエスト回数が10回に達した場合もThread.sleep(1)を実行する
-
マルチスレッド環境では、Redis分散ロックを使用する。Redisのsetnx()を使用し、ロックの有効期限を1秒(実際のロック時間は3秒に設定)とする。サードパーティへのリクエストDTOが10回に達したら、1回のリクエストとしてまとめる。同時に時間もチェックし、必要に応じてスリープさせる
参考資料:APIの1秒あたりのリクエスト制限
-
スライディングウィンドウを使用した実装(Redisを使用)
参考資料:スライディングウィンドウによるレート制限、および具体的な実装方法
注意:RedisのzremrangeByScore(key, '-inf', max)でウィンドウ開始前のデータを削除。zcard(key)で総数を計算。zadd(key, currentTime, max)でリクエストを追加
-
-
Executors.newFixedThreadPool(5)を直接使用せず、ThreadPoolExecutorを使用する理由
- FixedThreadPoolとSingleThreadPool:許可されるリクエストキューの長さがInteger.MAX_VALUEのため、大量のリクエストがキューに蓄積され、OOMが発生する可能性がある
- CachedThreadPoolとScheduledThreadPool:許可されるスレッド作成数がInteger.MAX_VALUEのため、大量のスレッドが作成され、OOMが発生する可能性がある
-
newFixedThreadPool作成時、LinkedBlockingQueueの長さが指定されないため、デフォルト容量はInteger.MAX_VALUEとなり、無限にスレッドをキューに挿入でき、OOMを引き起こす
public class MyThreadPoolDemo { public static void main(String[] args) { ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 2L, TimeUnit.SECONDS, // ブロッキングキューの長さを3に指定 new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy() ); // 10件のリクエストをシミュレート try { for (int i = 1; i <= 10; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 業務を処理中"); }); } } catch (Exception e) { e.printStackTrace(); } finally { threadPool.shutdown(); } } }
-
マルチスレッド環境でのトランザクションロールバック
- スレッドプールを使用し、Callableインターフェースを実装してデータを送信する
- service.invokeAll(callableList)の結果をList<Future>で取得する
- 1つでも失敗した場合、connection.rollback()を使用してロールバックする
参考資料: https://juejin.cn/post/7193672218625179705
-
ForkJoinを使用したマルチタスクで、データ結果を取得する方法
- ForkJoinPoolを作成する
- ForkJoinTaskを作成する。カスタムタスククラス(RecursiveTaskを実装し、computeメソッドをオーバーライド)を定義する
- forkJoinPool.submit(task)でタスクを送信する
-
Futureと同様に、get()メソッドで結果を取得する
public class ForkJoinTest { public static void main(String[] args) throws ExecutionException, InterruptedException { long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinDemo(1L, 100000000L); ForkJoinTask<Long> submit = forkJoinPool.submit(task); Long sum = submit.get(); long end = System.currentTimeMillis(); System.out.println(end - start); } } public class ForkJoinDemo extends RecursiveTask<Long> { private Long start; private Long end; // タスク分割の閾値 private static final Long THRESHOLD = 10000L; public ForkJoinDemo(Long start, Long end) { this.start = start; this.end = end; } @Override protected Long compute() { if ((end - start) < THRESHOLD) { Long sum = 0L; for (Long i = start; i <= end; i++) { sum += i; } return sum; } else { Long middle = (start + end) / 2; ForkJoinDemo task1 = new ForkJoinDemo(start, middle); task1.fork(); ForkJoinDemo task2 = new ForkJoinDemo(middle + 1, end); task2.fork(); return task1.join() + task2.join(); } } } -
参考資料: https://blog.csdn.net/myjess/article/details/121377597
-
ForkJoinPoolとThreadPoolExecutorの違い
- ForkJoinPoolは分割統治アルゴリズムとワークスティーリングを使用してタスクを分割する。ThreadPoolExecutorはタスクを順次実行する
- ForkJoinPoolはスレッド数を自動管理し、自動並列化を行う
- 参考資料: ForkJoinPoolとThreadPoolExecutorの比較と選択
-
複数のタスクを順序通りに実行する方法
- 複数のスレッドを作成し、各スレッドでstart()メソッドを呼び出した後、join()メソッドを呼び出す
- 例: 3つのスレッドT1、T2、T3を順序通りに実行するには?
-
並列タスクとCompletableFuture
- allOf()を使用して、すべての子タスクの完了を待機する
- 参考資料: CompletableFuture詳細解説
-
メインスレッドがすべての子タスクの完了を待ってから処理を続行する方法(複数の実装方法)
- 上記の項目8と類似
- マルチスレッドにおけるトークンバケツアルゴリズムとリーキーバケツアルゴリズム
- 参考資料: リーキーバケツとトークンバケツの違い
- GuavaのRateLimiterを使用したトークンバケツの作成: https://segmentfault.com/a/1190000044488582
プロジェクト、フレームワーク、アーキテクチャ
-
マイクロサービスアーキテクチャにおいて、既存プロジェクトを素早くマイクロサービスアーキテクチャにアップグレードする方法
- サービスの管理と分割
- アーキテクチャ設計、チーム編成、技術選定
- 参考資料: マイクロサービスアーキテクチャのサービス管理実装
-
Spring Bootの自動設定(AutoConfiguration)の読み込みプロセス
- Spring Boot自動設定の原理
-
Spring Bootでの複数データソース設定の処理
- 参考資料: https://cloud.tencent.com/developer/article/1708681
- 参考資料: Spring Boot + MyBatisでの複数データソース設定とトランザクション一貫性
- プロジェクトの期間が短い場合のアーキテクチャ設計
- シングルサインオン(SSO)のプロセス
-
OAuth2のプロセス
- 設定ファイルにOAuth2設定を追加する
- @EnableOAuthSsoアノテーションを追加する
- WebSecurityConfigurerAdapterを継承し、configureメソッドをオーバーライドしてHTTPリクエストのマッチングを行う
- RESTコントローラー層で@PreAuthorizeを使用してアクセス制御を行う
- 参考資料: https://blog.csdn.net/likun557/article/details/131098664
- 参考資料: Spring Boot 2 + Spring Security + OAuth2でのSSO実装
-
JWT
- ヘッダー、ペイロード、トークンの構成
- build()、verify()メソッドの使用
- 参考資料: JWT詳細チュートリアルと使用法
JVM
- JVMのトラブルシューティング手順
- JVMでよく使用されるチューニングパラメータ
HTTPおよび各種プロトコル
- SSLなどの各種プロトコル、HTTPとHTTPSの違い
Netty
- NIO、Nettyの概要
- NIOでChannelの結果を取得する方法
MySQL
- MySQLのテーブル設計原則(第三正規形など)
- B+ツリーインデックス
- SQL最適化の手順
- すべてのフィールドにインデックスを作成することの問題点
フロントエンド
- Element UI + Vue 3
- Vue 2とVue 3の違い
- ログイン後、元のURLにリダイレクトできる仕組み
- セッションとクッキーの保存場所
- フォームの扱い
- JSON以外のリクエスト形式
アーキテクチャ設計
- 技術アーキテクチャ図
- シーケンス図
- UML図(クラス図)
- ビジネスアーキテクチャ図
中軟国際(面接内容)
Spring AOPの機能実装
参考資料: Spring Boot – Spring AOPの原理と簡単な実装