2012-05-19 25 views
3

是否可以重寫此代碼以更好地使用處理器? 我有一個類,它在一個單獨的線程中執行固定週期的一些任務。有時這個過程可以暫停並恢復。目前我正在使用一個標誌暫停,它工作正常,但以這種方式循環仍然加載處理器,當進程暫停。有沒有可能解決這個問題?更高效的暫停循環方式想要

private boolean mIsCanceled = false; 
private boolean mIsPaused = true; // TODO more efficient for processor way of pausing is required 
private final Thread mTimerThread = new Thread(new Runnable() { 
    @Override 
    public void run() { 
     while(!mIsCanceled){ 
      try { 
       Thread.sleep(UPDATE_PERIOD); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      if (!mIsPaused){ 
       doStep(); 
      } 
     } 
    } 
}); 

public MyClass(){ 
    mTimerThread.start(); 
} 

private void pause(){ 
    mIsPaused = true; 
} 

private void resume(){ 
    mIsPaused = false; 
} 

private void doStep(){ 
    // Some code 
} 

請只提供我的代碼的替代實現。

P.S.環境是Android OS 2.2+

+1

增加'UPDATE_PERIOD'將減少CPU負載。 – dasblinkenlight

+0

爲什麼你說「以這種方式循環仍然加載處理器,當進程暫停」? Thread.sleep()應該在給定的時間內放棄執行,因此在那段時間內不會「加載處理器」。 –

+0

1.增加你的UPDATE_PERIOD對我不起作用 2.我希望我的任務等到上課暫停而不是檢查標誌並執行循環(即使我在循環中有睡眠) – Solvek

回答

5

可用的工具有:

wait/notify - 我們都試圖從這個古老的系統了。

Semaphore s - 一旦你的線程抓住了它,你將它拿着直到釋放,所以再次抓住它並不會阻塞。這意味着你不能在你自己的線程中暫停。

CyclicBarrier - 必須在每次使用時重新創建。

ReadWriteLock - 我的最愛。你可以儘可能多的線程暫停你,只有當他們都叫resume時你纔會恢復。如果你願意,你甚至可以暫停自己。

import java.util.concurrent.locks.ReadWriteLock; 
import java.util.concurrent.locks.ReentrantReadWriteLock; 

/** 
* PauseableThread is a Thread with pause/resume and cancel methods. 
* 
* The meat of the process must implement `step`. 
* 
* You can either extend this and implement `step` or use the factory. 
* 
* Note that I cannot extend Thread because my resume will clash with Thread's deprecated one. 
* 
* Usage: Either write a `Stepper` and run it in a `PausableThread` or extend `PausableThread` and call `blockIfPaused()` at appropriate points. 
*/ 
public abstract class PauseableThread implements Runnable { 
    // The lock. 
    // We'll hold a read lock on it to pause the thread. 
    // The thread will momentarily grab a write lock on it to pause. 
    // This way you can have multiple pausers using normal locks. 
    private final ReadWriteLock pause = new ReentrantReadWriteLock(); 
    // Flag to cancel the wholeprocess. 
    private volatile boolean cancelled = false; 
    // The exception that caused it to finish. 
    private Exception thrown = null; 

    @Override 
    // The core run mechanism. 
    public void run() { 
    try { 
     while (!cancelled) { 
     // Block here if we're paused. 
     blockIfPaused(); 
     // Do my work. 
     step(); 
     } 
    } catch (Exception ex) { 
     // Just fall out when exception is thrown. 
     thrown = ex; 
    } 
    } 

    // Block if pause has been called without a matching resume. 
    private void blockIfPaused() throws InterruptedException { 
    try { 
     // Grab a write lock. Will block if a read lock has been taken. 
     pause.writeLock().lockInterruptibly(); 
    } finally { 
     // Release the lock immediately to avoid blocking when pause is called. 
     pause.writeLock().unlock(); 
    } 

    } 

    // Pause the work. NB: MUST be balanced by a resume. 
    public void pause() { 
    // We can wait for a lock here. 
    pause.readLock().lock(); 
    } 

    // Resume the work. NB: MUST be balanced by a pause. 
    public void resume() { 
    // Release the lock. 
    pause.readLock().unlock(); 
    } 

    // Stop. 
    public void cancel() { 
    // Stop everything. 
    cancelled = true; 
    } 

    // start - like a thread. 
    public void start() { 
    // Wrap it in a thread. 
    new Thread(this).start(); 
    } 

    // Get the exceptuion that was thrown to stop the thread or null if the thread was cancelled. 
    public Exception getThrown() { 
    return thrown; 
    } 

    // Create this method to do stuff. 
    // Calls to this method will stop when pause is called. 
    // Any thrown exception stops the whole process. 
    public abstract void step() throws Exception; 

    // Factory to wrap a Stepper in a PauseableThread 
    public static PauseableThread make(Stepper stepper) { 
    StepperThread pauseableStepper = new StepperThread(stepper); 
    // That's the thread they can pause/resume. 
    return pauseableStepper; 
    } 

    // One of these must be used. 
    public interface Stepper { 
    // A Stepper has a step method. 
    // Any exception thrown causes the enclosing thread to stop. 
    public void step() throws Exception; 
    } 

    // Holder for a Stepper. 
    private static class StepperThread extends PauseableThread { 
    private final Stepper stepper; 

    StepperThread(Stepper stepper) { 
     this.stepper = stepper; 
    } 

    @Override 
    public void step() throws Exception { 
     stepper.step(); 
    } 
    } 

    // My test counter. 
    static int n = 0; 

    // Test/demo. 
    public static void main(String[] args) throws InterruptedException { 

    try { 
     // Simple stepper that just increments n. 
     Stepper s = new Stepper() { 
     @Override 
     public void step() throws Exception { 
      n += 1; 
      Thread.sleep(10); 
     } 
     }; 
     PauseableThread t = PauseableThread.make(s); 
     // Start it up. 
     t.start(); 
     Thread.sleep(1000); 
     t.pause(); 
     System.out.println("Paused: " + n); 
     Thread.sleep(1000); 
     System.out.println("Resuminng: " + n); 
     t.resume(); 
     Thread.sleep(1000); 
     t.cancel(); 
    } catch (Exception e) { 
    } 
    } 
} 

編輯:代碼修改爲更普遍的使用。

0

你可以通過使用監視器而不是睡覺線程來提高效率。您只需在代碼中使用同步的關鍵字創建塊。最後一個對象是監視器。在監視器的API中查看uP。

+0

看起來像監視器不是可用於Android。 – Solvek

+0

它在Java API中,我建議您閱讀它,如果你想:) http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html – Joelmob

1

你最好的選擇是要麼使用wait()/通知()或簡單地切換到ScheduledExecutorService

正確的wait()/通知()的使用可能會非常棘手。我強烈建議「實踐中的Java併發」來了解關於線程的更多信息。