「高い同時実行性」の問題を解決する方法に関する記事

「高い並行性」言えば、近年プログラミングの世界で人気のあるネットワーク用語と言えば十分です。結局のところ、インターネットの急速な発展、特に電子商取引プラットフォームアプリケーションの急速な発展に伴い、インターネットサービスのコンテンツはますます豊富になり、プラットフォームユーザーの数は増加しています。Taobao、Tmall、Jingdong、 " Pixixixi」や揺れる音などは、大勢の人が毎日使用しなければならないほぼ全国レベルのアプリケーションです。

 

これらのアプリケーションでは、「Tmall Double 11」、「Jingdong 618」、「Commodity seckill」、「Train ticket grabbing」などの大規模なイベントが、短期間に集中することが多く、多数あります。これらの瞬間に直面している同時訪問の数ユーザートラフィックとアクセスニーズの流入を適切に解決できない場合、それは以前のTmall Double 11クラッシュのようになり、ユーザーエクスペリエンスに影響を与えるだけでなく、通常のアクティビティを妨害し、売上高を大幅に削減します。 ..

 

そして、この種の高い同時実行性の問題を解決する方法、最初の基礎はスレッドで遊ぶことができることです。したがって、今日のコンテンツでは、スレッドに関する乾燥した知識を学びます。

 

1.なぜスレッドが必要なのですか?

 

栗を贈る:Baidu Netdiskアプリケーションを開き、アップロードおよびダウンロード機能を使用したいと思います。

 

スレッドがない場合、操作は次のようになります。ファイルをアップロードするとき、他のことはできません。他のものをダウンロードする前に正常にアップロードする必要があります。アップロードされたファイルは1つずつしかアップロードできません。非常に悪い経験になる

 

また、アップロードとダウンロードを同時に行い、複数のアップロードと複数のダウンロードを行う機能を実現したい場合はどうでしょうか。

 

そのとおり!スレッドを使用するだけです!

 

現在のオペレーティングシステムは、WindowsシリーズであろうとLinuxシリーズであろうと、基本的にマルチユーザーマルチタスクオペレーティングシステムであり、マルチタスクはマルチスレッドによって実現されます。マルチタスクの実行は「同時実行」とも呼ばれます。

 

2.プロセスとスレッドの違い

 

スレッドに関しては、最初にプロセスについて言及する必要があります。多くの人がプロセスはプログラムだと考えていますが、それは同じことではありませんか?

 

心配しないでください。プロセスの定義を見てみましょう。

 

●プロセスの概要

 

システム内で独立して実行できるプログラムはプロセスと呼ばれ、プロセスはCPU割り当てリソースの最小単位でもあります。

 

たとえば、私たちは毎日のWindowsプロセスに精通しています。

 

 

各プロセスには独自の独立したメモリスペースがあります。シングルコアCPUはシングルプロセス処理です。つまり、同時に処理できるプロセスは1つだけですが、システムはCPUの実行に限られた時間を割り当てることができます。各プロセス(CPUタイムスライスと呼ばれる)に対して、CPUのスイッチング速度が速すぎて認識をはるかに超えているため、CPUはこの期間中に特定のプロセスを実行し、次の期間に実行するために別のプロセスにジャンプする場合があります。裸眼の能力ですから、多くのプロセスが同時に実行されているように見えます。

 

マルチコアCPUは、複数のプロセスの実行を同時に実現できますが、スケジューリングの問題により、コアがタイムスライス内の複数のプロセスを呼び出す場合があります。

 

●スレッドの概要

 

スレッドは、プロセス内のプロセスを完了する実行タスクであり、プロセス内の実行パスであり、プロセスとメモリ空間を共有します。スレッドは、プログラムで実行される最小の単位です。

 

スレッド自体は単独で存在することはできず、プロセスで実行する必要があります。プロセスは複数のスレッドを持つことができ(たとえば、Javaプログラムには基本的にメインスレッドとGCスレッド[ガベージコレクター]などの2つ以上のスレッドがあります)、同じプロセスのすべてのスレッドがプロセスのリソースを共有します。

 

3.Javaでのスレッドの実装

 

前回の紹介でスレッドを知ったのですが、Javaでスレッドをどのように実装するのでしょうか。

 

実際、Javaでスレッドを実装する主な方法は3つあります。

 

●Threadクラスを継承します

●Runnableインターフェースを実装する

●Callableインターフェースを実装する

 

最初の2つの実装はより一般的であり、クラスとインターフェースもjava.longパッケージにあります。3番目のCallableはjava.util.concurrentパッケージにあります。その実装と違いを後半で紹介します。

 

最初の2つの実装を見てみましょう。

 

(1)Threadクラスを継承します

 

まず、ThreadクラスもRunnableインターフェイスを実装していることがわかります

 

 

Threadオブジェクトを作成して直接使用できるようにします。

 

使用手順:

 

A. Threadクラスを継承するか、Threadオブジェクトを直接作成します

B.runメソッドにスレッドタスクコードを実装する

C. Threadのstartメソッドを呼び出して、スレッドを開始します

 

コードは次のように表示されます。

 

新しいスレッドで直接使用することもできます。コードは次のとおりです。

 

 

分析:次のメソッドは、より単純に使用するためにクラスを定義する必要はなく、呼び出しが少ない場合に適しています。また、runメソッドはThreadクラスのスレッド開始後に実行されますが、スレッドのstartメソッドはstartメソッドです!

 

startメソッドとrunメソッドの違い:

 

runメソッドは純粋なThreadクラスの通常のメソッドですが、スレッドの開始時に呼び出されます。thread.run()のみを使用する場合は、スレッド実行メソッドを呼び出したが開始しなかったことを意味するだけです。新しいスレッド。コードは引き続きメインスレッドmainで実行されます。

 

startメソッド呼び出しは、新しいスレッドが作成され、スレッドが準備完了状態になることを意味します。スレッドがCPUタイムスライス処理を取得すると、Java仮想マシンはスレッドのrunメソッドを呼び出し、内部のタスクコードを実行します。スレッドは次のようになります。タスクコードの実行後に実行されます。終了します。また、起動時に開かれる新しいスレッドは独立した実行タスクであり、以下のコードはスレッドの実行が完了するのを待たずに実行を継続できます。

 

Threadクラスの使用に関する制限:

 

Threadクラスの実装は単純ですが、Threadはクラスであるため、クラスはJavaで単一の継承しか実行できないため、スレッドのスケーラビリティは低くなります。

 

(2)実行可能なインターフェースを実装する

 

Runnableは関数型インターフェースです(@FunctionalInterfaceによって変更され、抽象メソッドは1つだけです)。定義は非常に単純で、runメソッドは1つだけです。ソースコードは次のとおりです。

 

 

以前、Threadクラスが実際にRunableインターフェイスを実装することも紹介しました。タスクコードを実行するためにスレッドを開始するrunメソッドは、実際にはRunnableインターフェイスメソッドです。したがって、Threadクラスを使用してRunableインターフェイスを実装することにより、スレッドの拡張使用を実装できます。

 

使用手順:

 

A.Runnableインターフェースを実装するクラスを定義します

B.runメソッドにスレッドタスクコードを実装する

C. RunnableインターフェイスインスタンスオブジェクトをThread(Runnable r)構築メソッドに渡し、startメソッドを呼び出してスレッドを開始します

 

コードは次のように表示されます。

 

 

 分析:

Runnableインターフェースの実装を使用すると、インターフェースの特性をより多く実装できるため、Runnableインターフェースをより多くのクラスで実装でき、そのスケーラビリティはThreadよりも強力です。さらに、Runnableは渡されたときに作成されるスレッドであるため、 Thread構築メソッドとして、Runnableは依存する必要があります。Threadクラスで使用され、複数のThreadオブジェクトが同じRunnableインスタンスを使用できます。

 

RunnableのLambda式の使用:

 

Java8はLambda式を提供します。Lambda式を使用してRunable実装クラスインスタンスを作成する場合、クラスを定義する必要がないため、より便利になります。

 

コードは次のように実装されています。

 

4.マルチスレッドのクラシックケースチケット販売ケース

 

スレッドは、Threadクラスを継承し、開発時にRunnableインターフェイスを実装することで実装できますが、Threadクラスの継承には制限があります。Runnableインターフェースの実装は拡張が容易であり、Rnnnableインターフェースを実装するインスタンスは、複数のThreadオブジェクトで共有できるため、マルチスレッド処理リソースの解決がより便利になります。チケット販売のケースで説明しましょう:

 

チケットを販売するウィンドウが3つあり、合計100チケットですが、3つのウィンドウすべてが同時に販売され、100チケットが販売されるようにするには、どうすればよいでしょうか。

 

(1)スレッドクラスの実装

 

 

実行の結果:

 

 

結論として:

最後に、各ウィンドウで100チケット、合計300チケットが販売されたのに、合計で100チケットしか販売されなかったことがわかりました。3つのウィンドウスレッドt1、t2、およびt3はできないため、Threadクラスを継承するのは明らかに不便です。 100枚のチケットを共有します。リソースなので、それぞれ100枚を販売しました。

 

(2)実行可能なインターフェースの実装

 

 

実行の結果:

 

 

結論として:

 

Runnableインターフェースを使用すると、3つのウィンドウスレッドt1、t2、およびt3が、スレッドごとに100チケットを販売するのではなく、同時に100チケットを販売できることがわかりました。これは、t1、t2、およびt3がすべて同じRunnableを使用するためです。インスタンス。

 

明らかに、Runnableインターフェースの実装は、100のチケットリソースの共有を実現できますが、操作の結果から、プログラムが何度実行されても、常に2つまたは3つのスレッドが販売されるという紛らわしい問題が見つかります。同じチケット!

 

何が原因ですか?

 

実際、これはいわゆる「安全でないマルチスレッド同時アクセスの問題」です。

 

さて、この乾物共有の問題はここで終わります。次の章では、オブジェクトへのマルチスレッドアクセスが安全でない理由を紹介します。興味のある友人は波に注意を払うことができます。次の号でお会いしましょう〜

 

おすすめ

転載: blog.csdn.net/weixin_43802541/article/details/112323985