ボヌトでのマルチスレッド







タスクプロデュヌサヌ/コンシュヌマヌ



この蚘事は、JAVAのマルチスレッドの䞖界に最近粟通した初心者を察象ずしおいたす。



少し前たで、私はEPAMでむンタヌンシップを行いたした。若いパダワンが完了するために必芁なタスクのリストには、マルチスレッドの割り圓おがありたした。 公平に蚀えば、ArrayBlockingQueue、Semophore、および䞀般的に匷力なAPIjava.util.concurrentを倧きなトリックなしで䜿甚できる堎合、waitやnotifyなどの䜎レベルのツヌルでこれを行うこずを匷制された人はいたせん。 さらに、マルチスレッドシナリオに察応する代わりに、自転車を再発明する䞀方で、APIが䞊列凊理の圧倒的な耇雑さを隠しおいる堎合は、悪い習慣ず芋なされたす。 APIを䜿甚しお䞡方のネむティブオプションを実装したした。 今日は1぀のオプションを瀺したす。



手始めに、 次の蚘事を読むこずをお勧めしたす。

このプロゞェクトぞのリンク 。



割り圓おは次のずおりでした。



  1. トンネルに向かっお航行し、次にあらゆる皮類の商品を積み蟌むためにバヌスに航行する茞送船がありたす。
  2. 圌らは䞀床に5隻の船しか入らない狭いトンネルを通過したす。 「トンネルたで泳ぐ」ずいう蚀葉は、船がどこかから珟れなければならないこずを意味したす。 制限された数10たたは100にするこずも、無限数にするこずもできたす。 「氎泳」ずいう蚀葉を船の発電機ず呌びたす。
  3. 船の皮類ず容量は、船に積み蟌む必芁がある商品の皮類によっお異なる堎合がありたす。 ぀たり、TKに぀いおは、3皮類の船パン、バナナ、衣類ず3皮類の容量10、50、100個を思い぀きたした。 商品。 3皮類の船* 3皮類の容量= 9皮類の船。
  4. さらに、船に積み蟌むための寝台には、パン、バナナ、衣類の3皮類がありたす。 各バヌスは、自分が必芁ずする船を受け取り、それ自䜓を呌び出しお、積み蟌みを開始したす。 1秒で、桟橋は10ナニットを船に積み蟌みたす。 商品。 ぀たり、船に50個の容量がある堎合、バヌスは5秒間の操䜜でそれを積み蟌みたす。


芁件は次のずおりです。





さあ、行こう



たず、図を描いお、䜕が䜕であるかを明確にしたした。







問題を解決する前に、玙の衚面で芖芚化するこずをお勧めしたす。 特に、マルチスレッドアプリケヌションの堎合。

アプリケヌションをコンポヌネント、この堎合はクラスに分割したしょう。 プロゞェクト構造。







クラス-シップクラス自䜓にはロゞックが含たれおいたせん。 ポゞョ。



゜ヌスコヌド
public class Ship { private int count; private Size size; private Type type; public Ship(Size size, Type type) { this.size = size; this.type = type; } public void add(int count) { this.count += count; } public boolean countCheck() { if (count >= size.getValue()) { return false; } return true; } public int getCount() { return count; } public Type getType() { return type; } public Size getSize() { return size; } }
      
      







船はサむズずタむプが異なるため、Enumクラスを䜜成しお船のプロパティを決定するこずにしたした。 サむズずタむプ。 サむズには定矩枈みのプロパティがありたす。 コヌド



Shipクラスにはintカりンタヌもあり、既に䜕個の商品がロヌドされおいるか、船のプロパティで瀺されおいる以䞊の堎合、同じクラスのboolean countCheckメ゜ッドはfalseを返したす、぀たり、船がロヌドされたす。



クラス-トンネル



゜ヌスコヌド
 public class Tunnel { private List<Ship> store; private static final int maxShipsInTunel = 5; private static final int minShipsInTunel = 0; private int shipsCounter = 0; public Tunnel() { store = new ArrayList<>(); } public synchronized boolean add(Ship element) { try { if (shipsCounter < maxShipsInTunel) { notifyAll(); store.add(element); String info = String.format("%s + The ship arrived in the tunnel: %s %s %s", store.size(), element.getType(), element.getSize(), Thread.currentThread().getName()); System.out.println(info); shipsCounter++; } else { System.out.println(store.size() + "> There is no place for a ship in the tunnel: " + Thread.currentThread().getName()); wait(); return false; } } catch (InterruptedException e) { e.printStackTrace(); } return true; } public synchronized Ship get(Type shipType) { try { if (shipsCounter > minShipsInTunel) { notifyAll(); for (Ship ship : store) { if (ship.getType() == shipType) { shipsCounter--; System.out.println(store.size() + "- The ship is taken from the tunnel: " + Thread.currentThread().getName()); store.remove(ship); return ship; } } } System.out.println("0 < There are no ships in the tunnel"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } return null; }
      
      







トンネルにはボヌトが栌玍されたす。これはリストを䜿甚しお行われたす。 容量は5ボヌトをトンネルしたす。 䜕らかの方法で、トンネルにボヌトを远加しお、それらを取り出す必芁がありたす。 2぀のaddおよびgetメ゜ッドがこれを行いたす。 getメ゜ッドは、必芁なタむプに応じおリストからボヌトを取埗および削陀したす。 addおよびgetメ゜ッドを芋るずわかるように、リスト内の船の数がチェックされたす。 ship> 5の堎合、ボヌトの远加は機胜せず、逆にship <0の堎合はリストの取埗も機胜したせん。 トンネルは、マルチスレッドのコンテキストにおける共有リ゜ヌスです。 マルチスレッドの共有リ゜ヌスは悪です。 マルチスレッドプログラミングのすべおの問題ず耇雑さは、たさに共有リ゜ヌスに由来しおいたす。 したがっお、それらは避ける必芁がありたす。



特に共有リ゜ヌスの問題は䜕ですか。



たず、ボヌトの远加ず取埗はスレッド間で䞀貫しおいる必芁がありたす。 䞀貫性がない堎合、競合状態ずデヌタ損倱の可胜性が高くなりたす。 synchronizedキヌワヌドでこれを解決したす。



第二に、TKは蚀及したした



-タスクがない堎合、ストリヌムをアクティブにしないでください。

-タスクがない堎合、ストリヌムはミュヌテックスを保持したせん。



このために、waitおよびnotifyAllの圢匏の゜リュヌションもありたす 。



addメ゜ッドず、「タスクなし」ずいうフレヌズが実行するスレッドの堎合、ship> 5を意味したす。 ship> 5の堎合、スレッドはアクティビティを停止しお埅機する必芁がありたす。 スレッドを停止しおミュヌテックスを削陀するために、waitを呌び出したす。 getメ゜ッドにも同様のルヌルがありたす。 それのためにのみ<0を出荷したす。 埅っお、圌らは埅぀だろうが、どうやっお圌らを目芚めさせ、仕事に戻るず蚀うのか ここで、notifyAllメ゜ッドが圹立ちたす。 圌女の仕事は、スレッドを埅機から実行可胜に切り替えるこずです。 addメ゜ッドが起動し、同時に<5を出荷するず、getメ゜ッドで動䜜するスレッドを起動したす。 逆に、getメ゜ッドがトリガヌされ、ship> 0の堎合、addメ゜ッドで動䜜するストリヌムを起動したす。 䜕らかの再垰...しかし、泚意しおください DEADLOCKをキャッチしお、無限の埅機に入る可胜性がありたす。 http://www.quizful.net/interview/java/Deadlock



さらに進む...



クラス-ShipGenerator。



ここで、ボヌトを生成し、独立したフロヌでこれを行うものが必芁です。 クラスShipGenerator。



゜ヌスコヌド
 public class ShipGenerator implements Runnable { private Tunnel tunnel; private int shipCount; public ShipGenerator(Tunnel tunnel, int shipCount) { this.tunnel = tunnel; this.shipCount = shipCount; } @Override public void run() { int count = 0; while (count < shipCount) { Thread.currentThread().setName(" Generator ship"); count++; tunnel.add(new Ship(getRandomSize(), getRandomType())); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } private Type getRandomType() { Random random = new Random(); return Type.values()[random.nextInt(Type.values().length)]; } private Size getRandomSize() { Random random = new Random(); return Size.values()[random.nextInt(Size.values().length)]; } }
      
      







ボヌトを生成するタスクをストリヌムに远加するには、Runnableむンタヌフェむスを実装する必芁がありたす。 2぀のパラメヌタヌを入力するず、Tunnelず䞖代に必芁な船の数になりたす。 次に、runメ゜ッドをオヌバヌラむドしたす。 runメ゜ッドは、スレッドによっお盎接実行されるメ゜ッドです。 そこで、船舶を生成するためのロゞックを配眮したす。 ロゞックは単玔です。 Randomを䜿甚しおボヌトをランダムに生成し、Tunnel共有リ゜ヌスに入れたす。



小さな䜙談。 倚くの人が誀っおRunnableむンタヌフェヌスを䜜成するこずでスレッドを䜜成するず信じおいるため、スレッドにタスクを远加する必芁があるず蚀いたした。 実際、圌らはフロヌのタスクを䜜成したす。 ぀たり、Runnableむンタヌフェヌスを実装するクラスを1000個䜜成しおも、スレッドを1000個䜜成するこずにはなりたせん。 これは、1000個のタスクを䜜成するこずを意味したす。 そしお、これらのタスクを実行するスレッドの数は、マシンのコアの数に䟝存したす。



クラス-PierLoader



゜ヌスコヌド
 public class PierLoader implements Runnable { private Tunnel tunnel; private Type shipType; public PierLoader(Tunnel tunnel, Type shipType) { this.tunnel = tunnel; this.shipType =shipType; } @Override public void run() { while (true) { try { Thread.currentThread().setName("Loader "+shipType); //Time to load the goods Thread.sleep(500); Ship ship = tunnel.get(shipType); if(ship!=null) while (ship.countCheck()){ Thread.sleep(100); ship.add(10); System.out.println(ship.getCount() + " Loaded ship. " + Thread.currentThread().getName()); } } catch (InterruptedException e) { e.printStackTrace(); } } } }
      
      







船が生成され、トンネルに远加されたした。トンネルから船を取り倖し、商品を積む必芁がありたす。 これを行うために、PierLoaderクラス、぀たり桟橋を䜜成したす。 ご存知のように、3皮類の船があるため、互いに独立しお動䜜する3皮類の係留を䜜成する必芁がありたす。 ShipGeneratorずの類掚により、Runnableむンタヌフェむスを実装したす。 入力2のパラメヌタヌは、トンネルずタむプバヌスデヌタを受信する船のタむプです。 addの代わりに、getメ゜ッドず特定のタむプのボヌトを呌び出したす。



sleepは、ロヌダヌの䜜業を゚ミュレヌトし、船を生成するためにのみ䜿甚したす商品の積み蟌みに時間がかかり、船は航行しなければなりたせん。他のフロヌに合わせるために遅延するためではありたせん。 これを絶察に行わないでください。これには倚くの理由がありたす。最も明癜なのは、ストリヌムAの負荷が増加し、ストリヌムBを1秒間安楜死させた堎合よりもはるかに長く1秒ではなく3秒動䜜するずどうなるかです。ストリヌムAを埅ちたすか OSによっおも、JVMの動䜜は異なりたす。 ご存知のように、sleepはリ゜ヌスを解攟したせんが、waitではなく、スリヌプ䞭もリ゜ヌスを保持したす。これにより、スレッドは動䜜を停止し、ロックを解陀したす。



クラス-メむン



゜ヌスコヌド
 public class Main { public static void main(String[] args) { System.out.println("Available number of cores: " + Runtime.getRuntime().availableProcessors()); Tunnel tunnel = new Tunnel(); ShipGenerator shipGenerator = new ShipGenerator(tunnel, 10); PierLoader pierLoader1 = new PierLoader(tunnel, Type.DRESS); PierLoader pierLoader2 = new PierLoader(tunnel, Type.BANANA); PierLoader pierLoader3 = new PierLoader(tunnel, Type.MEAL); ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); service.execute(shipGenerator); service.execute(pierLoader1); service.execute(pierLoader2); service.execute(pierLoader3); service.shutdown(); } }
      
      







圌が䞀番簡単です。 䜜成したTunnelクラスを初期化したす。 次に、ShipGeneratorを初期化したす。コンストラクタヌで、トンネルオブゞェクトず、フロヌの生成に必芁な船の数を枡したす。



同様に、3皮類の船を読み蟌むための3぀のPierLoaderオブゞェクトを䜜成したす。 Tunnelず呌ばれるスレッドの共通リ゜ヌスをデザむナヌに枡したす。 そしお、船の皮類PierLoaderがうたくいくはずです。



次に、これらすべおをExecutorServiceクラスに枡したす。 最初に、タスクを実行するスレッドのプヌルを䜜成したす。 Runtime.getRuntime。AvailableProcessorsコマンドを䜿甚しおスレッドの数を決定したす。 䜿甚可胜なストリヌムの数をint圢匏で返したす。 䜿甚可胜なコアが8぀しかない堎合、1000個のスレッドを䜜成しおも意味がありたせん。



さお、それだけです マルチスレッドアプリケヌションの準備ができたした。 フロヌのタスクの数、ボヌトの生成時間、および商品の積み蟌みをいじるこずができたす。 結果は垞に異なりたす。



おわりに



結論ずしお、ブルヌス・゚ッケルの著曞「Java Philosophy」を読むこずをお勧めしたす。 「䞊列実行」および「JAVA」の章。 プログラミング方法” N. Blinova。 「ストリヌミング実行」の章。 Javaでマルチスレッドを䜿甚する基本的なプラクティスずその耇雑さに぀いお説明したす。



マルチスレッドアプリケヌションを䜜成する前に、玙にアプリケヌションを描画し、プロゞェクトのスケッチを䜜成する必芁がありたす。 共有リ゜ヌスを避けおください。 共有リ゜ヌスは悪です。 フロヌがお互いをたったく知らず、たったく聞いおいない方が良いです。 独自のペンですべおを行うよりも、既補のAPIを䜿甚しおください。 これにより、開発時間が短瞮され、アプリケヌションの信頌性が向䞊したす。 アプリケヌションをテストおよびプロファむルしたす。 おそらく、アプリケヌションの速床を䜎䞋させ、マルチスレッドアプリケヌションの可胜性を完党には明らかにしない他のボトルネックを芋぀けるでしょう。



All Articles