[TIL]スレッド同期とスケジューリング

4863 ワード


プロセスとスレッド


プロセス


プロセスは簡単に言えば実行中のプログラムです.
プログラムを実行すると、オペレーティングシステムは必要なリソースを割り当てて処理します.

山紅


各プロセスには少なくとも1つのスレッドが存在します.
2つ以上のスレッドを持つプロセスをマルチスレッドプロセスと呼びます.
Threedをプロセスファクトリでタスクを処理する労働者と見なすのは容易に理解できるはずだ.

マルチスレッドのメリットとデメリット


長所
  • CPUの利用率向上
  • リソースをより効率的に使用できます.
  • ユーザに対する応答能力を向上させる.
  • タスクは分離され、コードは簡潔です.
  • 短所
  • 複数のスレッドが1つのリソースを共有するため、同期、デッドロックなどの問題を考慮するために慎重にプログラミングする必要があります.
  • スヶジューリング


    まずThreedのLifeCycleについて知ります.

    全部で5つの状態がある.
    Newはスレッドを生成し、start()はまだ呼び出されていません.
    TerminatedはThreadの仕事が終わった状態です.
    重要なのはRunnable,Running,Blockedです.
    Runnableは待機中です.実行しなかった.
    言ったようにRunningは実行中です.
    blockdは、I/O演算、同期ブロック等によりブロックされた状態である.
    Timed Waitingは一時停止状態です.
    図に示すように、新しく作成したねじはすぐには動作しません.
    まずはスタンバイ状態に入ります.

    スレッドの実行を制御する方法


  • static void sleep(long mills)
    -指定された時間内にThreadを一時停止します.指定された時間が経過すると、自動的に待機状態に入ります.

  • void join(), join(long mills)
    -特定のスレッドが指定された時間内に実行されることを確認します.指定した時間が経過するか、タスクが終了すると、join()を呼び出すスレッドが戻って実行されます.

  • void interrupt()
    -sleep()またはjoin()は、停止中のスレッドを起動し、実行待ち状態にします.このトピックではInterruptedExceptionが発生し、一時停止状態から解放されます.

  • void stop()
    -直ちにThreedを停止します.

  • suspend()
    -ねじを一時停止します.recep()が呼び出されると、運転待ち状態に戻ります.

  • void resume()
    -(Depented)suspend()は、一時停止中のスレッドを実行待ち状態にします.

  • static void yield()
    -実行中に他のスレッドに自分の実行時間を譲ります.
  • スレッド同期


    マルチスレッドプロセスの場合、複数のスレッドは同じプロセス内のリソースを共有し、互いの操作に影響します.
    例えば、Threed AとThreed Bが同時に動作するとする.
    どちらもxが10のint型変数に対して4則演算を行う.
    Aはxを3、Bはxを3に減算します.
    私たちが望んでいる結果は10です.
  • Aはxを読み出す.
  • Aをxに3加算します.xに加えた値を保存したいです.
  • の間で、Bはxを読む.
  • Bはxから3を減算する.
  • Aは、Bが演算する間、xに13を格納する.
  • は、最後に、Bがxから3を減算した値を記憶する.
  • xの値は何ですか.
    ...
    ...
    7です.
    Aさんは仕事の途中でBさんが手を出したからです.
    A動作中は,他のスレッドが動作を妨害しないように同期する.
    では、コードで同期を実現するにはどうすればいいのでしょうか.
    synchronizedキーワードを使用すればいいです.

    synchronized


    Aがxに3を加え、xを加えると、
    他のスレッドを中間に挿入しないでください.
    次のコード
    // 이 메서드는 이제 임계 영역이다.
    public synchronized void sum(int y){
    	x += y;
    }
    このようにメソッドを宣言すると、メソッド全体が臨界領域になります.
    臨界領域とは、他のスレッドの挿入が許可されていない領域を指します.
    下図に示すように、方法の一部のみを臨界領域として指定できます.
    public void sum(int y){
    	// 쓰레드가 this의 락을 얻게 한다.
    	synchronized(this){
        		x += y;
            }
    }
    ()の参照変数は、ロックするオブジェクトを参照する必要があります.
    このブロックをsynchronizedブロックと呼ぶ.
    ブロックに入る領域から、Threadは()で指定されたオブジェクトのlockを取得し、ブロックから離れるとlockを返します.
    各オブジェクトにはロックがあり、そのオブジェクトロックを持つスレッドのみが臨界領域のコードを実行できます.
    他のスレッドはロックの取得を待機します.

    wait()とnotify()


    あるThreedがロックを持って空回りを続けると
    他のスレッドはそのオブジェクトのロックを待っていて、他の操作もうまくいかない.
    wait()この場合、Threedに先にロックを返して待機させます.
    notify()は、待機しているスレッドがrockを再取得して動作することを許可する.
    notify()が呼び出されると、待機プールで待機しているスレッドが通知されます.
    結局、運が本当に悪かったら、一度も知らせをもらえずに永遠に待っていたThreedがあったかもしれません.

    飢餓と競争状態


    飢餓現象は前述のような現象である.
    オブジェクトの待機プールで待機しているスレッドは、長い間lockを取得できない可能性があります.
    この現象を阻止するにはnotifyAll()を使用する必要があります.
    複数のスレッドをnotifyAll()で通知すると、これらのスレッドは競合してロックを取得します.これを競争状態と呼ぶ.
    この問題を解決するにはjavaを使用します.util.concurrent.locksパッケージで提供されるlockクラスを利用すればよい.

    volatileキーワード


    カーネルは、メモリから読み込まれた値をキャッシュに格納し、キャッシュから値を読み込むことで動作します.
    再び値を読み込む場合は、まずキャッシュに値があるかどうかを確認し、値がない場合にのみメモリから値を読み出す.
    そのため、メモリに格納されている値とキャッシュに格納されている値が異なる場合があります.
    volatileキーワードを変数に貼り付け、nicoreが変数の値を読み出すと、キャッシュから読み出すのではなくメモリから読み出す.
    これにより、キャッシュとメモリの値が一致しないことが解決されます.
    ちなみに、スレッドがsynchronizedブロックに入ると現れると、キャッシュとメモリの間でも同期されます.