検索
ホームページJava&#&チュートリアルJavaの再起動可能スレッドとスレッドプールクラスの設計(詳細説明)

JAVA マルチスレッド プログラミングを知っている人なら誰でも、スレッドを生成するには 2 つの方法があることを知っています。1 つは、クラスが Thread クラスを直接継承し、その run() メソッドを実装する方法です。もう 1 つは、クラスが Runnable インターフェイスを実装し、 run() メソッドを使用して、次の形式のように、このクラスをコンストラクター パラメーターとして使用して新しいスレッドを作成します: Thread t=new Thread(myRunnable)。最後に、Thread クラスの start() メソッドを実行することでスレッドが開始されます。

JAVA では、スレッドの実行が終了すると、つまり run() メソッドを実行した後は再起動できません。この時点で、このスレッド オブジェクトは不要なオブジェクトとなり、ガベージ コレクターによるリサイクルを待っています。次回このスレッドを再度開始する場合は、新しいスレッド オブジェクトを作成して再度開始する必要があります。オブジェクトの頻繁な作成と破棄は、操作効率に影響を与えるだけでなく、不要なスレッド オブジェクトをリサイクルする時間がないため、大量のガベージ メモリが生成される可能性があります。この影響は、ストレージ容量と処理速度が比較的限られているモバイル プラットフォームでは特に大きくなります。 。では、オブジェクトを頻繁に作成したり破棄したりせずに繰り返し開始できるようにスレッド クラスを再設計することはできるでしょうか?

もちろんです。次に、この「再開可能スレッド」クラスの設計を紹介します。

まず第一に、スレッドに実行させたいタスクをスレッドの run() メソッドに直接入れても、とにかく目的を達成できないことを明確にする必要があります。 JAVA スレッド クラスが実行されると、 run() メソッドが完了すると、再度開始することはできません。したがって、唯一の実現可能な方法は、ユーザー プログラム (「ユーザー プロセス」と呼ばれる場合もあります) の run() メソッドを、ユーザー プロセスが実行されるときにスレッドの実際の run() メソッド内の while ループ本体に入れることです。待って 。スレッドを再起動するために restart メソッドが呼び出されると、待機中のスレッドが実際に起動されて、次の while ループが開始されます。大まかな考え方が決まり、次のコードは理解しやすいです:

public class ReusableThread implements Runnable {
 //线程状态监听者接口
 public interface ThreadStateListener {
  public abstract void onRunOver(ReusableThread thread);//当用户过程执行完毕后调用的方法
 }
  
 public static final byte STATE_READY=0; //线程已准备好,等待开始用户过程
 public static final byte STATE_STARTED=1; //用户过程已启动
 public static final byte STATE_DESTROYED=2; //线程最终销毁
  
 byte mState; //标示可重启线程的当前状态
  
 Thread mThread; //实际的主线程对象
 Runnable mProc; //用户过程的run()方法定义在mProc中
 ThreadStateListener mListener; //状态监听者,可以为null
  
 /** Creates a new instance of ReusableThread */
 public ReusableThread(Runnable proc) {
  mProc = proc;
  mListener = null;
  mThread = new Thread(this);
  mState = STATE_READY;
 }
  
 public byte getState() {return mState;}
  
 public void setStateListener(ThreadStateListener listener) {
  mListener = listener;
 }
  
 /**可以在处于等待状态时调用该方法重设用户过程*/
 public synchronized boolean setProcedure(Runnable proc) {
  if (mState == STATE_READY) {
   mProc = proc;
   return true;
  }
  else
   return false;
 }
  
 /**开始执行用户过程*/
 public synchronized boolean start() {
  if (mState == STATE_READY) {
   mState = STATE_STARTED;
   if (!mThread.isAlive()) mThread.start();
   notify(); //唤醒因用户过程执行结束而进入等待中的主线程
   return true;
  }
  else
   return false;
 }
  
 /**结束整个线程,销毁主线程对象。之后将不可再次启动*/
 public synchronized void destroy() {
  mState = STATE_DESTROYED;
  notify();
  mThread = null;
 }
  
 public void run() {
  while (true) {
   synchronized (this) {
    try {
     while (mState != STATE_STARTED) {
      if (mState == STATE_DESTROYED) return;
      wait();
     }
    } catch(Exception e) {e.printStackTrace();}
   }
    
   if (mProc != null) mProc.run();
   if (mListener != null) mListener.onRunOver(this); //当用户过程结束后,执行监听者的onRunOver方法
    
   synchronized (this) {
    if (mState == STATE_DESTROYED) return;
    mState = STATE_READY;
   }
  }
 }
  
}

コードは理解しやすいですよね。ただし、「ステータス リスナー」インターフェイスが存在する理由を説明してください。追加の処理を実行できるように、ユーザー プロセスの終了後にタイムリーな通知を取得したい場合があります。この場合、ステータス リスナーの onRunOver メソッドが役立ちます。直感的な例としては、以下で説明する「スレッド プール」クラスでは、ユーザー プロセスの実行後に再起動可能なスレッドを自動的にプールに戻す必要があります。このとき、プールに戻るアクションを onRunOver メソッドに配置できます。 、そのパラメータは再起動可能なスレッド オブジェクトであるため、パラメータで示されたオブジェクトはスレッド プールにリサイクルできます。

スレッド プール クラスに関しては、実際には前述のオブジェクト プール クラスのサブクラスであり、その中のすべてのオブジェクトは ReusableThread クラスに属します。さらに、ReusableThread.ThreadStateListener インターフェイスを実装しているので、ユーザー プロセスの終了時に通知を受け、スレッドをリサイクルする作業を実行できます。

public class ThreadPool extends ObjectPool implements ReusableThread.ThreadStateListener {
 public static final int DefaultNumThreads = 16; //默认池容量
  
 public ReusableThread getThread() {
  return (ReusableThread)fetch();
 }
  
 public void onRunOver(ReusableThread thread) {
  recycle(thread); //当用户过程结束时,回收线程
 }
  
 private void init(int size) {
  ReusableThread thread;
  //初始化线程池内容
  for (int i=0; i<size; i++) {
   thread = new ReusableThread(null);
   thread.setStateListener(this);
   setElementAt(thread, i);
  }
 }
  
 public ThreadPool(int size) {
  super(size);
  init(size);
 }
  
 public ThreadPool() {
  super(DefaultNumThreads);
  init(DefaultNumThreads);
 }
  
}

もちろん、追加する必要がある関数がいくつかあります。通常のスレッドよりスレッドが 1 つだけ多いため、再起動可能な「拡張」スレッド クラスの場合、スレッドの sleep() など、元の Thread クラスの関数も使用できる必要があります。ただし、これらは比較的単純なので、ここでは省略します。

以下のテストプログラムを書きます。スレッド プール クラスを使用する代わりに、オブジェクト プール内のオブジェクトが属するクラス CharEmitter で Runnable インターフェイスとスレッドを実装し、オブジェクト プール クラスと再起動可能なスレッド クラスの結合テストを実行する予定です。ステータス リスナー インターフェイス。スレッド プール オブジェクトには含まれないが、独立して使用される再起動可能なスレッド メンバー オブジェクトが含まれます。このスレッド (CharEmitter クラスで定義) のユーザー プロセスが終了すると、onRunOver メソッドは、この CharEmitter オブジェクトをプールにリサイクルするアクションを実行します。これは、オブジェクト プールとの違いは onRunOver でリサイクル アクションを実行することであるため、スレッド プール クラスを間接的にテストする役割も果たします。

わかりやすくするために、コードに直接アクセスすることをお勧めします:

TestThreadPool.java :

/**字符放射器*/
class CharEmitter implements Runnable, ReusableThread.ThreadStateListener {
 char c; //被发射的字符
 boolean[] isEmitting; //标示某字符是否正被发射(直接以字符对应的ASCII码作下标索引)
 
 ReusableThread thread; //可重启线程对象
 
 ObjectPool myHomePool; //为知道应把自己回收到哪里,需要保存一个到自己所在对象池的引用
 
 CharEmitter(ObjectPool container, boolean[] isCharEmitting) {
  isEmitting=isCharEmitting;
  myHomePool=container;
  thread=new ReusableThread(this); //新建可重启线程对象,设其用户过程为CharEmitter类自己定义的
 }
 
 /**开始“发射”字符*/
 public void emit(char ch) {
  //字符被要求只能是&#39;0&#39;到&#39;9&#39;之间的数字字符
  if (ch>=&#39;0&#39; && ch<=&#39;9&#39;) {
   c=ch;
  }
  else c=&#39; &#39;;
  
  thread.start(); //启动线程
 }
 
 public void run() {
  if (c==&#39; &#39;) return; //若不是数字字符直接结束
  //为便于观察,不同数字之前的空格数目不同,以便将其排在不同列上
  int spaceLen=c-&#39;0&#39;;
  StringBuffer s=new StringBuffer(spaceLen+1);
  for (int i=0; i<spaceLen; i++) s.append(&#39; &#39;);
  s.append(c);
  
  while (isEmitting[c]) {
    System.out.println(s); //不断地向屏幕写字符
  }
 }
 
/**实现线程状态监听者接口中的方法*/
 public void onRunOver(ReusableThread t) {
  myHomePool.recycle(this); //回收自身入池
 }
}
 
 
 
public class TestThreadPool {
 
public static void main(String[] args) {
 // TODO Auto-generated method stub
 //标示字符是否正被发射的标志变量数组
 boolean[] isEmitting=new boolean[256];
 for (int i=0; i<256; i++) isEmitting[i]=false;
  
 ObjectPool emitters=new ObjectPool(10); //新建对象池,容量为10
 for (int i=0; i<10; i++) {
 //用CharEmitter对象填满池子
 emitters.setElementAt(new CharEmitter(emitters, isEmitting), i);
 }
  
 byte[] c=new byte[1];
 CharEmitter emitter;
  
 while(true) {
 try {
 System.in.read(c); //从键盘读入一个字符,以回车键表示输入结束
 } catch(Exception e) {e.printStackTrace();}
  
 if (isEmitting[c[0]]) {
 isEmitting[c[0]]=false; //若字符正被发射,则结束其发射
 }
 else {
 isEmitting[c[0]]=true;
 emitter=(CharEmitter)emitters.fetch(); //向池中索取一个CharEmitter对象
 emitter.emit((char)c[0]); //发射用户输入的字符
 }
 }
}
 
}

実行後、キーボードから 0 から 9 までの任意の数字を入力して Enter キーを押すと、画面がスクロールし続けます。番号を表示するには、同じ番号を再度入力すると、番号は表示されなくなります。複数の数値が同時に送信されると、異なる数値の表示が交互に表示されることが明らかにわかります。これはまさに、スレッド間での仮想マシンのスケジューリングの結果です。実行結果は、設計したクラス関数が完全に正しいことを示しています。

後で説明する J2ME の Bluetooth 通信の補助クラスでは、スレッド プールと再起動可能なスレッドがかけがえのない役割を果たすことがわかります。

上記の Java 再起動可能スレッドとスレッド プール クラスの設計 (詳細な説明) は、編集者によって共有されたすべての内容です。参考にしていただければ幸いです。また、PHP 中国語 Web サイトをサポートしていただければ幸いです。

Java の再起動可能なスレッドとスレッド プール クラスの設計に関する関連記事 (詳細な説明) については、PHP 中国語 Web サイトに注目してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
Javaのクラスロードメカニズムは、さまざまなクラスローダーやその委任モデルを含むどのように機能しますか?Javaのクラスロードメカニズムは、さまざまなクラスローダーやその委任モデルを含むどのように機能しますか?Mar 17, 2025 pm 05:35 PM

Javaのクラスロードには、ブートストラップ、拡張機能、およびアプリケーションクラスローダーを備えた階層システムを使用して、クラスの読み込み、リンク、および初期化が含まれます。親の委任モデルは、コアクラスが最初にロードされ、カスタムクラスのLOAに影響を与えることを保証します

カフェインやグアバキャッシュなどのライブラリを使用して、Javaアプリケーションにマルチレベルキャッシュを実装するにはどうすればよいですか?カフェインやグアバキャッシュなどのライブラリを使用して、Javaアプリケーションにマルチレベルキャッシュを実装するにはどうすればよいですか?Mar 17, 2025 pm 05:44 PM

この記事では、カフェインとグアバキャッシュを使用してJavaでマルチレベルキャッシュを実装してアプリケーションのパフォーマンスを向上させています。セットアップ、統合、パフォーマンスの利点をカバーし、構成と立ち退きポリシー管理Best Pra

Javaで機能的なプログラミング技術を実装するにはどうすればよいですか?Javaで機能的なプログラミング技術を実装するにはどうすればよいですか?Mar 11, 2025 pm 05:51 PM

この記事では、Lambda式、Streams API、メソッド参照、およびオプションを使用して、機能プログラミングをJavaに統合することを調べます。 それは、簡潔さと不変性を通じてコードの読みやすさと保守性の改善などの利点を強調しています

キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPA(Java Persistence API)を使用するにはどうすればよいですか?キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPA(Java Persistence API)を使用するにはどうすればよいですか?Mar 17, 2025 pm 05:43 PM

この記事では、キャッシュや怠zyなロードなどの高度な機能を備えたオブジェクトリレーショナルマッピングにJPAを使用することについて説明します。潜在的な落とし穴を強調しながら、パフォーマンスを最適化するためのセットアップ、エンティティマッピング、およびベストプラクティスをカバーしています。[159文字]

高度なJavaプロジェクト管理、自動化の構築、依存関係の解像度にMavenまたはGradleを使用するにはどうすればよいですか?高度なJavaプロジェクト管理、自動化の構築、依存関係の解像度にMavenまたはGradleを使用するにはどうすればよいですか?Mar 17, 2025 pm 05:46 PM

この記事では、Javaプロジェクト管理、自動化の構築、依存関係の解像度にMavenとGradleを使用して、アプローチと最適化戦略を比較して説明します。

非ブロッキングI/OにJavaのNIO(新しい入出力)APIを使用するにはどうすればよいですか?非ブロッキングI/OにJavaのNIO(新しい入出力)APIを使用するにはどうすればよいですか?Mar 11, 2025 pm 05:51 PM

この記事では、単一のスレッドで複数の接続を効率的に処理するためにセレクターとチャネルを使用して、非ブロッキングI/O用のJavaのNIO APIについて説明します。 プロセス、利点(スケーラビリティ、パフォーマンス)、および潜在的な落とし穴(複雑さ、

適切なバージョン化と依存関係管理を備えたカスタムJavaライブラリ(JARファイル)を作成および使用するにはどうすればよいですか?適切なバージョン化と依存関係管理を備えたカスタムJavaライブラリ(JARファイル)を作成および使用するにはどうすればよいですか?Mar 17, 2025 pm 05:45 PM

この記事では、MavenやGradleなどのツールを使用して、適切なバージョン化と依存関係管理を使用して、カスタムJavaライブラリ(JARファイル)の作成と使用について説明します。

ネットワーク通信にJavaのソケットAPIを使用するにはどうすればよいですか?ネットワーク通信にJavaのソケットAPIを使用するにはどうすればよいですか?Mar 11, 2025 pm 05:53 PM

この記事では、ネットワーク通信のためのJavaのソケットAPI、クライアントサーバーのセットアップ、データ処理、リソース管理、エラー処理、セキュリティなどの重要な考慮事項をカバーしています。 また、パフォーマンスの最適化手法も調査します

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

SublimeText3 英語版

SublimeText3 英語版

推奨: Win バージョン、コードプロンプトをサポート!

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

WebStorm Mac版

WebStorm Mac版

便利なJavaScript開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

SublimeText3 Linux 新バージョン

SublimeText3 Linux 新バージョン

SublimeText3 Linux 最新バージョン