javamultithreadingthread-safetyandroid-handlerthread

HandlerThread safety in Android


Ok, this is a question I didn't think needed asking but either its the long hours or whatnot, but my mind is foggy so here it goes.

Creating a class with a HandlerThread, Looper, and Handler:

public class MyClass {
    //private volatile boolean mRunning   = false;
    private boolean mRunning              = false;
    private HandlerThread mHandlerThread  = null;
    private Handler mMessageHandler       = null;

    public static final int MESSAGE_START = 1;
    public static final int MESSAGE_STOP  = 2;

    public MyClass() {
        mHandlerThread = new HandlerThread("com.test.myclass");
        mHandlerThread.start();
        mMessageHandler = new MessageHandler(mHandlerThread.getLooper());
    }

    private class MessageHandler extends Handler {
        public MessageHandler(Looper looper) {
            super(looper);
        }

        private void start() {
            mRunning = true;
        }

        private void stop() {
            mRunning = false;
        }

        @Override
        public void handleMessage(final Message msg) {
            try {
                switch (msg.what) {
                    case MESSAGE_START:
                        start();
                        break;

                    case MESSAGE_STOP:
                        stop();
                        break;

                    default:
                        throw new RuntimeException("Invalid message: " + msg.what);
                }
            } catch (RuntimeException e) {
                stop();
            }
        }
    }

    public void release() {
        if (isRunning()) {
            stop();
        }

        // PS: is this a good way to stop HandlerThead/Looper in API =< 17 ?
        if (mHandlerThread != null) {
            mHandlerThread.quit();
            mHandlerThread = null;
        }
    }

    // Should this be a synchronized method
    public boolean isRunning() {
        return mRunning;
        /**
         * Or should the variable be synchronized itself?
         * synchronized(mRunning) { return mRunning; }
         */

        // Or just use a semaphore?
    }

    public void start() {
        mMessageHandler.sendEmptyMessage(MESSAGE_START);
    }

    public void stop() {
        mMessageHandler.sendEmptyMessage(MESSAGE_STOP);
    }
}

So mRunning is accessed by both threads (main and looper). As such, the access should be synchronized. Which way would you choose? Making the variable volatile (so that both threads have up-to-date local values), making the isRunning method synchronized? Or accessing variable via synchronized(mRunning){...}?

Finally, implementation of release method, is that per standard? Or any other way you'd choose?


Solution

  • If contention for the lock is low, then synchronizing on a lock will cost about the same as a volatile access. If conention is high, then the volatile version probably will perform better.

    You can't do this though:

    synchronized(mRunning) { ... }  //ERROR!
    

    mRunning is a boolean expression. You can't synchronize on a boolean value, you can only synchronize on an Object.