Andorid之用ConditionVariable实现线程同步

一、学习ConditionVariable之前的复习

如果你不懂wait()、notify()怎么使用,最好先复习下我之前的这篇博客,怎么使用wait()、notify()实现生产者和消费者的关系


java之wait()、notify()实现非阻塞的生产者和消费者

 
二、看下ConditionVariable源代码实现

    package android.os;
     
    /**
     * Class that implements the condition variable locking paradigm.
     *
     * <p>
     * This differs from the built-in java.lang.Object wait() and notify()
     * in that this class contains the condition to wait on itself.  That means
     * open(), close() and block() are sticky.  If open() is called before block(),
     * block() will not block, and instead return immediately.
     *
     * <p>
     * This class uses itself as the object to wait on, so if you wait()
     * or notify() on a ConditionVariable, the results are undefined.
     */
    public class ConditionVariable
    {
        private volatile boolean mCondition;
     
        /**
         * Create the ConditionVariable in the default closed state.
         */
        public ConditionVariable()
        {
            mCondition = false;
        }
     
        /**
         * Create the ConditionVariable with the given state.
         *
         * <p>
         * Pass true for opened and false for closed.
         */
        public ConditionVariable(boolean state)
        {
            mCondition = state;
        }
     
        /**
         * Open the condition, and release all threads that are blocked.
         *
         * <p>
         * Any threads that later approach block() will not block unless close()
         * is called.
         */
        public void open()
        {
            synchronized (this) {
                boolean old = mCondition;
                mCondition = true;
                if (!old) {
                    this.notifyAll();
                }
            }
        }
     
        /**
         * Reset the condition to the closed state.
         *
         * <p>
         * Any threads that call block() will block until someone calls open.
         */
        public void close()
        {
            synchronized (this) {
                mCondition = false;
            }
        }
     
        /**
         * Block the current thread until the condition is opened.
         *
         * <p>
         * If the condition is already opened, return immediately.
         */
        public void block()
        {
            synchronized (this) {
                while (!mCondition) {
                    try {
                        this.wait();
                    }
                    catch (InterruptedException e) {
                    }
                }
            }
        }
     
        /**
         * Block the current thread until the condition is opened or until
         * timeout milliseconds have passed.
         *
         * <p>
         * If the condition is already opened, return immediately.
         *
         * @param timeout the maximum time to wait in milliseconds.
         *
         * @return true if the condition was opened, false if the call returns
         * because of the timeout.
         */
        public boolean block(long timeout)
        {
            // Object.wait(0) means wait forever, to mimic this, we just
            // call the other block() method in that case.  It simplifies
            // this code for the common case.
            if (timeout != 0) {
                synchronized (this) {
                    long now = System.currentTimeMillis();
                    long end = now + timeout;
                    while (!mCondition && now < end) {
                        try {
                            this.wait(end-now);
                        }
                        catch (InterruptedException e) {
                        }
                        now = System.currentTimeMillis();
                    }
                    return mCondition;
                }
            } else {
                this.block();
                return true;
            }
        }
    }


 
三、我们分析怎么使用

  比如有多个线程需要执行同样的代码的时候,我们一般希望当一个线程执行到这里之后,后面的线程在后面排队,然后等之前的线程执行完了再让这个线程执行,我们一般用synchronized实现,但是这里我们也可以用ConditionVariable实现,从源码可以看到,我们初始化可以传递一个boolean类型的参数进去,我们可以传递true进去

      public ConditionVariable(boolean state)
      {
          mCondition = state;
      }

然后你看下ConditionVariable类里面这个方法

        public void block()
        {
            synchronized (this) {
                while (!mCondition) {
                    try {
                        this.wait();
                    }
                    catch (InterruptedException e) {
                    }
                }
            }
        }

如果第一次初始化的时候mCondition是true,那么第一次调用这里就不会走到wait函数,然后我们应该需要一个开关让mCondition变成false,让第二个线程进来的时候我们应该让线程执行wait()方法,阻塞在这里,这不看下ConditionVariable类里面这个函数

        public void close()
        {
            synchronized (this) {
                mCondition = false;
            }
        }

这不恰好是我们需要的,我们可以马上调用这个函数close(),然后让程序执行我们想执行的代码,最后要记得调用open方法,如下

       public void open()
        {
            synchronized (this) {
                boolean old = mCondition;
                mCondition = true;
                if (!old) {
                    this.notifyAll();
                }
            }
        }

因为这里调用了notifyAll方法,把之前需要等待的线程呼唤醒

所以我们使用可以这样使用

1、初始化

ConditionVariable mLock = new ConditionVariable(true);

2、同步的地方这样使用

       mLock.block();
       mLock.close();
       /**
          你的代码
        **/
       mLock.open();

 
 
四、测试代码分析

我先给出一个原始Demo

 

    public class MainActivity extends ActionBarActivity {
     
        public static final String TAG = "ConditionVariable_Test";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            for (int i = 0; i < 10; i++) {
                new Mythread("" + i).start();
            }
        }
        public int num = 5;
        class Mythread extends Thread {
            String name;
            public Mythread(String name) {
                this.name = name;
            }
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num--;
                    if (num >= 0)
                        Log.i(TAG, "thread name is:" + name + " num is:" + num);
                    else
                        break;
                }
            }
        }
    }

 

运行的结果是这样的:

 

    ConditionVariable_Test  I  thread name is:0 num is:4
                             I  thread name is:1 num is:3
                             I  thread name is:2 num is:2
                             I  thread name is:3 num is:1
                             I  thread name is:4 num is:0

很明显不是我们想要的结果,因为我想一个线程进来了,需要等到执行完了才让另外一个线程才能进来

 

我们用ConditionVariable来实现下

    package com.example.conditionvariable;
     
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    import android.os.Bundle;
    import android.os.ConditionVariable;
    import android.support.v7.app.ActionBarActivity;
    import android.util.Log;
     
    public class MainActivity extends ActionBarActivity {
     
        public static final String TAG = "ConditionVariable_Test";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mCondition = new ConditionVariable(true);
            for (int i = 0; i < 10; i++) {
                new Mythread("" + i).start();
            }
        }
        public int num = 5;
        class Mythread extends Thread {
            String name;
            public Mythread(String name) {
                this.name = name;
            }
            @Override
            public void run() {
                mCondition.block();
                mCondition.close();
                while (true) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num--;
                    if (num >= 0)
                        Log.i(TAG, "thread name is:" + name + " num is:" + num);
                    else
                        break;
                }
                mCondition.open();
            }
        }
    }

运行的结果如下

    onditionVariable_Test  I  thread name is:0 num is:4
                             I  thread name is:0 num is:3
                             I  thread name is:0 num is:2
                             I  thread name is:0 num is:1
                             I  thread name is:0 num is:0

很明显这是我想要的效果,还有其它办法吗?当然有

我们还可以使用ReentrantLock重入锁,代码修改如下

    public class MainActivity extends ActionBarActivity {
     
        public static final String TAG = "ConditionVariable_Test";
        private Lock lock = new ReentrantLock();
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            for (int i = 0; i < 10; i++) {
                new Mythread("" + i).start();
            }
        }
        public int num = 5;
        class Mythread extends Thread {
            String name;
            public Mythread(String name) {
                this.name = name;
            }
            @Override
            public void run() {
                lock.lock();
                while (true) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num--;
                    if (num >= 0)
                        Log.i(TAG, "thread name is:" + name + " num is:" + num);
                    else
                        break;
                }
                lock.unlock();
            }
        }
    }

 
 

运行的结果如下

    onditionVariable_Test  I  thread name is:0 num is:4
                             I  thread name is:0 num is:3
                             I  thread name is:0 num is:2
                             I  thread name is:0 num is:1
                             I  thread name is:0 num is:0

很明显这是我想要的效果,还有其它办法吗?当然有,那就是用synchronized同步块,代码改成如下

    package com.example.conditionvariable;
     
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
     
    import android.os.Bundle;
    import android.os.ConditionVariable;
    import android.support.v7.app.ActionBarActivity;
    import android.util.Log;
     
    public class MainActivity extends ActionBarActivity {
     
        public static final String TAG = "ConditionVariable_Test";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            for (int i = 0; i < 10; i++) {
                new Mythread("" + i).start();
            }
        }
        public int num = 5;
        class Mythread extends Thread {
            String name;
            public Mythread(String name) {
                this.name = name;
            }
            @Override
            public void run() {
                synchronized (MainActivity.class) {
                    while (true) {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        num--;
                        if (num >= 0)
                            Log.i(TAG, "thread name is:" + name + " num is:" + num);
                        else
                            break;
                    }
                }
            }
        }
    }

运行的结果如下

    onditionVariable_Test  I  thread name is:0 num is:4
                             I  thread name is:0 num is:3
                             I  thread name is:0 num is:2
                             I  thread name is:0 num is:1
                             I  thread name is:0 num is:0

很明显这是我想要的效果

 
 
五、总结

在Android开发里面我们一般实现线程通过可以用ConditionVariable、ReentrantLock(重入锁)、synchronized、阻塞队列(ArrayBlockingQueue、LinkedBlockingQueue)
   put(E e) : 在队尾添加一个元素,如果队列满则阻塞
   size() : 返回队列中的元素个数
   take() : 移除并返回队头元素,如果队列空则阻塞

 


 


 



  作者:chen.yu
深信服三年半工作经验,目前就职游戏厂商,希望能和大家交流和学习,
微信公众号:编程入门到秃头 或扫描下面二维码
零基础入门进阶人工智能(链接)