JAVA 다중 스레드 프로그래밍을 아는 사람이라면 스레드를 생성하는 두 가지 방법이 있다는 것을 알고 있습니다. 하나는 클래스가 Thread 클래스를 직접 상속하고 해당 run() 메서드를 구현하는 것이고, 다른 하나는 클래스가 Runnable 인터페이스를 구현하는 것입니다. run() 메서드를 구현한 다음 이 클래스를 생성자 매개변수로 사용하여 다음 형식과 유사한 새 Thread를 만듭니다. Thread t=new Thread(myRunnable). 마지막으로 Thread 클래스의 start() 메소드를 실행하여 스레드를 시작합니다.
JAVA에서는 스레드 실행이 끝나면, 즉 run() 메서드를 실행한 후에는 다시 시작할 수 없습니다. 현재 이 스레드 개체는 쓸모 없는 개체가 되어 가비지 수집기의 재활용을 기다리고 있습니다. 다음에 이 스레드를 다시 시작하려면 새 스레드 개체를 만들고 다시 시작해야 합니다. 객체의 빈번한 생성과 소멸은 운영 효율성에 영향을 줄 뿐만 아니라 쓸모없는 스레드 객체는 재활용할 시간이 없기 때문에 대량의 가비지 메모리를 생성할 수도 있습니다. 이러한 영향은 저장 공간과 처리 속도가 상대적으로 제한된 모바일 플랫폼에서 특히 중요합니다. . 그렇다면 객체를 자주 생성하고 파괴하지 않고도 반복적으로 시작할 수 있도록 스레드 클래스를 다시 설계할 수 있습니까?
물론이죠. 다음으로 이 "재시작 가능한 스레드" 클래스의 디자인을 소개하겠습니다.
우선, 여전히 스레드가 수행하길 원하는 작업을 스레드의 run() 메소드에 직접 넣으면 어차피 목적을 달성할 수 없다는 점을 분명히 해야 합니다. 위에서 언급한 것처럼 JAVA 스레드 클래스는 run() 메서드를 실행한 후에는 다시 시작할 수 없습니다. 따라서 가능한 유일한 방법은 사용자 프로그램("사용자 프로세스"라고도 함)의 run() 메서드를 스레드의 실제 run() 메서드 내부에 있는 while 루프 본문에 넣는 것입니다. 기다리다 . 스레드를 다시 시작하기 위해 재시작 메서드가 호출되면 실제로 다음 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); } }
물론 필요한 몇 가지 기능이 있습니다. 스레드에는 다시 시작할 수 있는 추가 "향상된" 스레드 클래스가 있으므로 스레드의 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) { //字符被要求只能是'0'到'9'之间的数字字符 if (ch>='0' && ch<='9') { c=ch; } else c=' '; thread.start(); //启动线程 } public void run() { if (c==' ') return; //若不是数字字符直接结束 //为便于观察,不同数字之前的空格数目不同,以便将其排在不同列上 int spaceLen=c-'0'; StringBuffer s=new StringBuffer(spaceLen+1); for (int i=0; i<spaceLen; i++) s.append(' '); 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 사이의 숫자를 입력하고 뒤로 자동차를 누르면 해당 번호가 화면에 계속 스크롤됩니다. 동일한 번호를 다시 입력하면 해당 번호가 더 이상 표시되지 않습니다. 여러 숫자가 동시에 전송되면 서로 다른 숫자가 인터리브되어 표시되는 것을 분명히 볼 수 있습니다. 이는 바로 스레드 간 가상 머신 스케줄링의 결과입니다. 실행 결과는 우리가 설계한 클래스 함수가 완전히 정확하다는 것을 보여줍니다.
나중에 논의할 J2ME의 블루투스 통신을 위한 보조 클래스에서는 스레드 풀과 재시작 가능 스레드가 대체할 수 없는 역할을 한다는 것을 알 수 있습니다.
Java 재시작 가능 스레드 및 스레드 풀 클래스 설계에 대한 위 기사(자세한 설명)는 모두 편집자가 공유한 내용이므로 참고할 수 있기를 바라며, PHP를 지원해 주시길 바랍니다. 중국사이트.
Java 재시작 가능 스레드 및 스레드 풀 클래스 설계에 대한 더 많은 관련 기사(자세한 설명)를 보려면 PHP 중국어 웹사이트에 주목하세요!