Home  >  Article  >  Java  >  Design of java restartable threads and thread pool classes (detailed explanation)

Design of java restartable threads and thread pool classes (detailed explanation)

高洛峰
高洛峰Original
2017-01-23 16:18:051385browse

Anyone who knows JAVA multi-thread programming knows that there are two ways to generate a thread. One is that the class directly inherits the Thread class and implements its run() method; the other is that the class implements the Runnable interface and implements its run() method. , and then create a new Thread with this class as the constructor parameter, similar to the following form: Thread t=new Thread(myRunnable). In the end, the thread is started by executing the start() method of the Thread class.

In JAVA, once a thread has finished running, that is, after executing its run() method, it cannot be restarted. At this time, this thread object has become a useless object, waiting for recycling by the garbage collector. The next time you want to start this thread again, you must create a new thread object and start it again. Frequently creating and destroying objects not only affects operating efficiency, but may also generate a large amount of garbage memory because useless thread objects have no time to be recycled. This impact is particularly significant on mobile platforms where storage space and processing speed are relatively limited. So, can you redesign a thread class so that it can be started repeatedly without frequently creating and destroying objects?

sure. Next, I will introduce the design of this "restartable thread" class.

First of all, it must be clear that if you still put the task you want the thread to do directly in the run() method of the thread, you will not be able to achieve the purpose anyway, because as mentioned above, JAVA Once the thread class has executed the run() method, it cannot be started again. So the only feasible way is to put the run() method of the user program (maybe called "user process") into the while loop body inside the thread's actual run() method, and make the thread wait after the user process is executed. . When the restart method is called to restart a thread, it actually wakes up the waiting thread to start the next while loop. The general idea is determined, and the following code is easy to understand:

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;
   }
  }
 }
  
}

The code is easy to understand, right? But explain why there is a "status listener" interface. Sometimes we may want to get a timely notification after the user process ends so that we can perform additional processing. In this case, the onRunOver method of the status listener is useful. An intuitive example is that in the "thread pool" class mentioned below, a restartable thread should be automatically returned to the pool after executing a user process. At this time, the action of returning to the pool can be placed in the onRunOver method. , and its parameter is the restartable thread object, so the object indicated by the parameter can be recycled into the thread pool.

As for the thread pool class, it is actually a subclass of the previously mentioned object pool class, and all the objects in it are of the ReusableThread class. In addition, it implements the ReusableThread.ThreadStateListener interface so that it can be notified in time when the user process ends and perform the work of recycling the thread:

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);
 }
  
}

Of course, there are some functions that may need to be added, because since it is just more efficient than ordinary The thread has an additional "enhanced" thread class that can be restarted, so the functions of the original Thread class should also be available, such as the thread's sleep(). However, those are relatively simple and will be omitted here.

Write the test program below. I plan to proceed like this: instead of using the thread pool class, I conduct a joint test on the object pool class and the restartable thread class. The class CharEmitter to which the object in the object pool belongs implements the Runnable interface and the thread status listener interface, and contains A restartable thread member object that is not included in any thread pool object but is used independently. When the user process of this thread (defined in the CharEmitter class) ends, the onRunOver method performs the action of recycling this CharEmitter object into the pool. This also plays the role of indirectly testing the thread pool class, because the difference from the object pool is that it performs recycling actions in onRunOver.

Let’s make it clear by going directly to the code:

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]); //发射用户输入的字符
 }
 }
}
 
}

After execution, type any number between 0 and 9 from the keyboard and press back car, the number will continue to scroll on the screen; if you enter the same number again, the number will no longer be displayed. When multiple numbers are transmitted at the same time, it can be clearly seen that the display of different numbers is interleaved. This is precisely the result of the virtual machine scheduling between threads. The running results show that the class function we designed is completely correct.

In the auxiliary classes for Bluetooth communication in J2ME that will be discussed later, we will see that thread pools and restartable threads play an irreplaceable role.

The above article on the design of java restartable threads and thread pool classes (detailed explanation) is all the content shared by the editor. I hope it can give you a reference, and I also hope that everyone will support the PHP Chinese website.

For more relevant articles on the design of java restartable threads and thread pool classes (detailed explanation), please pay attention to the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn