JavaRush /Java Blog /Random-JA /レベル 26。レベルのトピックに関するインタビューの質問への回答。パート 2. 質問 6 ~ 9、11 ~ 12
zor07
レベル 31
Санкт-Петербург

レベル 26。レベルのトピックに関するインタビューの質問への回答。パート 2. 質問 6 ~ 9、11 ~ 12

Random-JA グループに公開済み
レベル 26。レベルのトピックに関するインタビューの質問への回答。 パート 2. 質問 6 ~ 9、11 ~ 12 - 1

6. カンカレンシとは何ですか?

同時実行性は、複数のスレッド間で動作するように最適化された特別なクラスを含む Java のクラス ライブラリです。これらのクラスは 1 つのパッケージにまとめられていますjava.util.concurrent。それらは、次のように機能に従って概略的に分類できます。 同時 レベル 26。レベルのトピックに関するインタビューの質問への回答。 パート 2. 質問 6 ~ 9、11 ~ 12 - 2コレクションjava.util-パッケージの標準のユニバーサル コレクションよりもマルチスレッド環境で効率的に動作するコレクションのセット。コレクション全体へのアクセスをブロックする基本的なラッパーの代わりにCollections.synchronizedList、データ セグメントのロックが使用されるか、待機なしアルゴリズムを使用してデータの並列読み取り用に作業が最適化されます。 キュー- マルチスレッドをサポートするノンブロッキング キューとブロッキング キュー。ノンブロッキング キューは、スレッドをブロックせずに速度と操作を行えるように設計されています。ブロッキング キューは、キューが空かオーバーフローしている、または空きの「コンシューマー」がないなど、いくつかの条件が満たされない場合に、「プロデューサー」または「コンシューマー」スレッドの「速度を下げる」必要がある場合に使用されます。 シンクロナイザーは、スレッドを同期するための補助ユーティリティです。これらは「並列」コンピューティングにおける強力な武器です。 エグゼキュータ- スレッド プールの作成、非同期タスクのスケジュール設定、および結果の取得のための優れたフレームワークが含まれています。 ロック-基本 的なsynchronizedスレッド同期メカニズムと比較して、より柔軟な代替スレッド同期メカニズムを表します。アトミックス- プリミティブと参照に対するアトミック操作をサポートするクラス。 ソース:waitnotifynotifyAll

7. カンカレンシのどの授業を知っていますか?

この質問に対する答えは、この記事に完全に記載されています。すべてをここに再掲することに意味がないと思うので、私が簡単に知ることができた光栄なクラスについてのみ説明します。 ConcurrentHashMap<K, V> - およびHashtableのブロックとはsynhronized異なりHashMap、データはキーのハッシュに分割されたセグメントの形式で表示されます。その結果、データは単一のオブジェクトではなくセグメントによってアクセスされます。さらに、イテレータは特定の期間のデータを表し、 をスローしませんConcurrentModificationExceptionAtomicBoolean、AtomicInteger、AtomicLong、AtomicIntegerArray、AtomicLongArray - クラス内で、型の 1 つの単純な変数へのアクセスを同期する必要がある場合はどうすればよいでしょうかintsynchronizedアトミック操作を使用する場合は、 およびを使用して構成を使用できますset/getvolatileしかし、新しいクラスを使用すると、さらに優れた結果を得ることができますAtomic*。CAS を使用しているため、これらのクラスの操作は、 を介して同期する場合よりも高速ですsynchronized/volatile。さらに、増加/減少だけでなく、指定された量ずつアトミックに追加するメソッドもあります。

8. ConcurrentHashMap クラスはどのように機能しますか?

導入の時点までに、ConcurrentHashMapJava 開発者は次のハッシュ マップ実装を必要としていました。
  • スレッドの安全性
  • アクセス中にテーブル全体をロックしない
  • 読み取り操作を実行するときにテーブル ロックがないことが望ましい
主な実装アイデアはConcurrentHashMap次のとおりです。
  1. マップ要素

    要素とは異なりHashMapEntryin はConcurrentHashMapとして宣言されますvolatileこれは、 JMMの変更による重要な機能です。

    
    static final class HashEntry<K, V> {
        final K key;
        final int hash;
        volatile V value;
        final HashEntry<K, V> next;
    
        HashEntry(K key, int hash, HashEntry<K, V> next, V value) {
            this .key = key;
            this .hash = hash;
            this .next = next;
            this .value = value;
         }
    
        @SuppressWarnings("unchecked")
        static final <K, V> HashEntry<K, V>[] newArray(int i) {
            return new HashEntry[i];
        }
    }
  2. ハッシュ関数

    ConcurrentHashMap改良されたハッシュ関数も使用されます。

    HashMapJDK 1.2ではどうだったかを思い出してください。

    
    static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

    ConcurrentHashMap JDK 1.5 からのバージョン:

    
    private static int hash(int h) {
        h += (h << 15) ^ 0xffffcd7d;
        h ^= (h >>> 10);
        h += (h << 3);
        h ^= (h >>> 6);
        h += (h << 2) + (h << 14);
        return h ^ (h >>> 16);
    }

    なぜハッシュ関数をより複雑にする必要があるのでしょうか? ハッシュ マップ内のテーブルの長さは 2 の累乗で決まります。バイナリ表現が下位と上位で変わらないハッシュ コードの場合、衝突が発生します。ハッシュ関数の複雑さを増すとこの問題が解決され、マップ内で衝突が発生する可能性が減ります。

  3. セグメント

    マップは N 個の異なるセグメントに分割されます (デフォルトでは 16、最大値は 16 ビットで 2 の累乗です)。各セグメントは、マップ要素のスレッドセーフなテーブルです。セグメントの数を増やすと、変更操作が複数のセグメントにまたがるようになり、実行時にブロックされる可能性が低くなります。

  4. 同時実行レベル

    このパラメータは、メモリ カードの使用量とカード内のセグメントの数に影響します。

    セグメントの数は、concurrencyLevel より大きい 2 の最も近い累乗として選択されます。concurrencyLevel を下げると、書き込み時にスレッドがマップ セグメントをブロックする可能性が高くなります。インジケーターを過大評価すると、メモリの非効率な使用につながります。1 つのスレッドだけがマップを変更し、残りのスレッドが読み取りを行う場合は、値 1 を使用することをお勧めします。

  5. 合計

    主な利点と実装機能は次のとおりですConcurrentHashMap

    • マップには次のようなhashmapインタラクション インターフェイスがあります。
    • 読み取り操作はロックを必要とせず、並行して実行されます。
    • 多くの場合、書き込み操作はブロックせずに並行して実行できます。
    • concurrencyLevel作成時に、読み取りおよび書き込みの統計によって決定される必要なものが示されます。
    • valueマップ要素には次のように宣言された値があります。volatile
    出典: ConcurrentHashMap の仕組み

9. ロッククラスとは何ですか?

共有リソースへのアクセスを制御するには、同期されたオペレーターの代わりにロックを使用できます。ロック機能は にパッケージ化されていますjava.util.concurrent.locks。まず、スレッドは共有リソースへのアクセスを試みます。フリーの場合は、スレッドにロックが設定されます。作業が完了すると、共有リソースのロックが解放されます。リソースが空いておらず、すでにロックが設定されている場合、スレッドはこのロックが解放されるまで待機します。Lockロック クラスは、次のメソッドを定義する インターフェイスを実装します。
  • void lock():ロックが取得されるまで待機します
  • boolean tryLock():ロックの取得を試みます。ロックが取得された場合は、trueを返します。ロックが取得されない場合は、falseを返します。メソッドとは異なり、lock()ロックが利用できない場合でもロックの取得を待機しません。
  • void unlock():ロックを解除します
  • Condition newCondition():Condition現在のロックに関連付けられているオブジェクトを返します
一般的な場合のロックの構成は非常に単純です。ロックを取得するには、メソッドが呼び出されlock()、共有リソースの操作が終了した後、メソッドが呼び出されunlock()、ロックが解放されます。このオブジェクトを使用Conditionすると、ブロックを管理できます。原則として、ロックを操作するには、ReentrantLockパッケージのクラスが使用され、java.util.concurrent.locks.このクラスは インターフェイス を実装しますLock。例として小さなプログラムを使用して、Java Lock API の使用を見てみましょう。つまり、Resourceいくつかのスレッド セーフ メソッドと、スレッド セーフが必要ないメソッドを含むクラスがあるとします。

public class Resource {
 
    public void doSomething(){
        // пусть здесь происходит работа с базой данных 
    }
     
    public void doLogging(){
        // потокобезопасность для логгирования нам не требуется
    }
}
Runnable次に、インターフェイスを実装し、クラス メソッドを使用する クラスを考えてみましょうResource

public class SynchronizedLockExample implements Runnable{
 
    // экземпляр класса Resource для работы с методами
    private Resource resource;
     
    public SynchronizedLockExample(Resource r){
        this.resource = r;
    }
     
    @Override
    public void run() {
        synchronized (resource) {
            resource.doSomething();
        }
        resource.doLogging();
    }
}
次に、 の代わりに Lock API を使用して上記のプログラムを書き直してみましょうsynchronized

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
// класс для работы с Lock API. Переписан с приведенной выше программы,
// но уже без использования ключевого слова synchronized
public class ConcurrencyLockExample implements Runnable{
 
    private Resource resource;
    private Lock lock;
     
    public ConcurrencyLockExample(Resource r){
        this.resource = r;
        this.lock = new ReentrantLock();
    }
     
    @Override
    public void run() {
        try {
            // лочим на 10 секунд
            if(lock.tryLock(10, TimeUnit.SECONDS)){
            resource.doSomething();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            //убираем лок
            lock.unlock();
        }
        // Для логгирования не требуется потокобезопасность
        resource.doLogging();
    }
 
}
プログラムからわかるように、tryLock()スレッドが一定時間だけ待機するようにするメソッドを使用しています。オブジェクトのロックを取得できない場合は、単にログを記録して終了します。もう一つ重要な点があります。try-finallyメソッドがdoSomething()例外をスローした 場合でもロックが確実に解放されるようにするには、ブロックを使用する必要があります。出典:

11. ミューテックスとは何ですか?

ミューテックスは、スレッド/プロセスを同期するための特別なオブジェクトです。ビジー状態とフリー状態の 2 つの状態を取ることができます。簡単に説明すると、ミューテックスは、busy (true) と free (false) の 2 つの値を取るブール変数です。スレッドがオブジェクトの排他的所有権を必要とする場合、スレッドはそのミューテックスをビジーとしてマークし、その処理が終了すると、そのミューテックスをフリーとしてマークします。ミューテックスは Java のすべてのオブジェクトに付加されます。Java マシンのみがミューテックスに直接アクセスできます。プログラマからは隠蔽されます。

12. モニターとは何ですか?

モニターは特別なメカニズム (コードの一部)、つまりミューテックス上のアドオンであり、ミューテックスの正しい動作を保証します。結局のところ、オブジェクトがビジーであることをマークするだけでは十分ではなく、他のスレッドがそのビジー オブジェクトを使用しようとしないようにする必要もあります。Java では、モニターはキーワード を使用して実装されますsynchronized。同期ブロックを作成すると、Java コンパイラーはそれを 3 つのコードに置き換えます。
  1. ブロックの先頭にsynchronized、ミューテックスをビジーとしてマークするコードが追加されます。
  2. ブロックの最後に、synchronizedミューテックスを空きとしてマークするコードが追加されます。
  3. ブロックの前に、synchronizedミューテックスがビジーかどうかをチェックするコードが追加されます。ミューテックスがビジーである場合、スレッドはミューテックスが解放されるまで待機する必要があります。
パート1
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION