使用notify / wait在Java中停止/重新启动线程

时间:2012-08-17 01:21:44

标签: java android multithreading synchronize

我正在为Android 2.2+开发简单的视频录制应用程序,并且无法让Timer线程按预期工作。代码如下,步骤如下:

  1. 当用户按下开始录制按钮时,开始录制并调用fightTimer.start()方法。
  2. 它调用timer.start()方法开始运行线程。 timer是fightTimer中的线程对象
  3. 当用户点击停止按钮时,调用方法stop()。在那里我设置了一个标志stopTimer = true,这样就可以阻止线程方法运行并调用timer.wait()方法让线程等待
  4. 当用户点击开始录制按钮时,再次调用fightTimer.start()方法。它调用线程timer.start(),因为计时器已经启动,但它会抛出异常。
  5. 我捕获异常并调用fightTimer.restart()方法。这个方法将我在步骤3中设置的标志stopTimer设置为true,所以现在我们有stopTimer = true。计时器线程仍然在run()方法内部等待
  6. 然后它调用timer.notify()让计时器知道它不再需要等待并且可以继续运行
  7. 现在我期待定时器线程再次开始运行但由于某种原因,此时执行跳转到调用notify()(restart())的同一方法的开头并设置标志stopTimer = false,然后尝试再次通知计时器线程。它抛出运行时异常,就在它结束的地方。
  8. 我认为我对线程的整个同步的理解是不正确的,所以如果有人能指出我搞砸的地方会很棒。下面是FightTimeris的代码,我甚至没有在logCat中获得任何输出信息。就像我说的任何帮助都会非常感激,因为我不明白为什么会发生这种情况以及如何解决它。

        public class FightTimer extends SurfaceView implements Runnable, SurfaceHolder.Callback {
    
            public Thread timer;
            public SurfaceHolder surfaceHolder;
            public Canvas canvas;
            public int counter=0;
            public volatile boolean stopTimer=false;
            public volatile boolean pauseTimer=false;
    
            public FightTimer(Context context)
            {
                super(context);
    
                surfaceHolder=getHolder();
    
                // this is necessary to make surfaceview transparent
                surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
                setZOrderOnTop(true);
    
                surfaceHolder.addCallback(this);
    
                timer=new Thread(this);
            }
    
    
            public void start()
            {
                try
                {
                    timer.start();
                }
                catch(IllegalThreadStateException ex)
                {
                    reStart();
                }
            }
    
            public synchronized void reStart()
            {
    // here the method is executed twice as I described in step 7
    // after notify() it actually jumps back to stopTimer=false again and then exits the function. Then outside of this object I catch RuntimeException         
                stopTimer=false;
                        timer.notify(); 
            }
    
            public synchronized void pause()
            {
                pauseTimer=true;
            }
    
            public synchronized void resume()
            {
                pauseTimer=false;
                timer.notify(); 
            }
    
            public void stop()
            {
                stopTimer=true;
            }
    
            public void run() {
    
                TimeWatch timeWatch=TimeWatch.start();
    
                Paint paint=new Paint();
    
                paint.setColor(Color.RED);
    
                paint.setTypeface(Typeface.create("Arial",Typeface.NORMAL));
                paint.setTextSize(20);
    
                while(true)
                {
                    // this is to pause timer
    
                    try
                    {
                        if(pauseTimer)
                        {
                            synchronized(timer) {
                                while(pauseTimer)
                                    timer.wait();
                            }
    
                            // TODO heres the code should be executed when timer is resumed eg.
                            // maybe calculate how timer should count now as it wasn't counting for a while etc
    
                        }
                    } catch(InterruptedException ex)
                    {
    
                    }
    
                    // this is to pause timer
    
                    try
                    {
                        if(stopTimer)
                        {
                            synchronized(timer) {
                                while(stopTimer)
                                    timer.wait();
                            }
    
                            // TODO heres the code should be executed when timer is restarted
                            // maybe reset timer completely etc
    
                            timeWatch.reset();
                        }
                    } catch(InterruptedException ex)
                    {
    
                    }
    
                    canvas=surfaceHolder.lockCanvas();
    
                    // canvas might not exists at this point as we might be in activitis onStop() callback and stopping preview
                    // etc. so we need to check if so then we exit run function
    
                    if(canvas==null) return;
    
                    //canvas.drawARGB(0,255,255,255);
                    canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); 
    
                    long minutes=timeWatch.time(TimeUnit.SECONDS)/60;
    
                    canvas.drawText(counter+"      "+minutes+":"+timeWatch.time(TimeUnit.SECONDS)%60,0,counter%60, paint);
    
                    counter++;
    
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
    
            }
    
            public void surfaceChanged(SurfaceHolder holder, int format, int width,
                    int height) {
                // TODO Auto-generated method stub
    
                //Toast.makeText(getContext(), "Surface Changed", Toast.LENGTH_LONG).show();
    
            }
    
            public void surfaceCreated(SurfaceHolder holder) {
                // TODO Auto-generated method stub
    
                //timer.start();
            }
    
            public void surfaceDestroyed(SurfaceHolder holder) {
                // TODO Auto-generated method stub
                // when surface is destroyed it means it cannot be displayed anymore and there is no canvas to draw
                // meaning the run() method cannot draw anything and calls to surfaceHolder will throw exception
                // so we need to stop thread here
                // this will happen when activity is in onStop() callback and when is already invisible and we are going to 
                // remove the object anyway so we don't care what will happenn later and make it wait. All we need is stop
                // run() from calling any other methods on canvas from surfaceHolder 
    
    
                Toast.makeText(getContext(), "Surface Destroyed", Toast.LENGTH_LONG).show();
            }
    
            public void setSurfaceHolder(SurfaceHolder surfaceHolder2) {
                // TODO Auto-generated method stub
                surfaceHolder=surfaceHolder2;
            }
    
        }
    

    还请在restart()方法中查看已编辑的评论。下面是调用堆栈,当退出restart()方法时。如果需要更多信息,请告诉我。

    DalvikVM[localhost:8754]    
        Thread [<1> main] (Suspended)   
            <VM does not provide monitor information>   
            MyFirstAppActivity.startRecording(View) line: 271   
            Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]  
            Method.invoke(Object, Object...) line: 521  
            View$1.onClick(View) line: 2077 
            Button(View).performClick() line: 2461  
            View$PerformClick.run() line: 8888  
            ViewRoot(Handler).handleCallback(Message) line: 587 
            ViewRoot(Handler).dispatchMessage(Message) line: 92 
            Looper.loop() line: 123 
            ActivityThread.main(String[]) line: 4627    
            Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]  
            Method.invoke(Object, Object...) line: 521  
            ZygoteInit$MethodAndArgsCaller.run() line: 858  
            ZygoteInit.main(String[]) line: 616 
            NativeStart.main(String[]) line: not available [native method]  
        Thread [<7> Binder Thread #2] (Running) 
        Thread [<6> Binder Thread #1] (Running) 
        Thread [<8> Binder Thread #3] (Running) 
        Thread [<9> Thread-9] (Running) 
    

1 个答案:

答案 0 :(得分:1)

方法reStart()和resume()必须在对象计时器上调用notify()之前获取监视器。 (类似于在上面的代码()中完成timer.wait()的方式。)

public synchronized void reStart()
{
// here the method is executed twice as I described in step 7
// after notify() it actually jumps back to stopTimer=false again and then exits the function. Then outside of this object I catch RuntimeException 
    stopTimer=false;
    synchronized(timer) {
        timer.notify();
    }
}

public synchronized void resume()
{
    pauseTimer=false;
    synchronized(timer) {
        timer.notify();
    }
}

如果不这样做,会在JRE上产生 IllegalMonitorStateException 。而stacktrace也发布了类似问题的观点。