Andorid之用ConditionVariable實(shí)現(xiàn)線(xiàn)程同步

一、學(xué)習(xí)ConditionVariable之前的復(fù)習(xí)

如果你不懂wait()、notify()怎么使用,最好先復(fù)習(xí)下我之前的這篇博客,怎么使用wait()、notify()實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者的關(guān)系


java之wait()、notify()實(shí)現(xiàn)非阻塞的生產(chǎn)者和消費(fèi)者

 
二、看下ConditionVariable源代碼實(shí)現(xiàn)

    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;
            }
        }
    }


 
三、我們分析怎么使用

  比如有多個(gè)線(xiàn)程需要執(zhí)行同樣的代碼的時(shí)候,我們一般希望當(dāng)一個(gè)線(xiàn)程執(zhí)行到這里之后,后面的線(xiàn)程在后面排隊(duì),然后等之前的線(xiàn)程執(zhí)行完了再讓這個(gè)線(xiàn)程執(zhí)行,我們一般用synchronized實(shí)現(xiàn),但是這里我們也可以用ConditionVariable實(shí)現(xiàn),從源碼可以看到,我們初始化可以傳遞一個(gè)boolean類(lèi)型的參數(shù)進(jìn)去,我們可以傳遞true進(jìn)去

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

然后你看下ConditionVariable類(lèi)里面這個(gè)方法

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

如果第一次初始化的時(shí)候mCondition是true,那么第一次調(diào)用這里就不會(huì)走到wait函數(shù),然后我們應(yīng)該需要一個(gè)開(kāi)關(guān)讓mCondition變成false,讓第二個(gè)線(xiàn)程進(jìn)來(lái)的時(shí)候我們應(yīng)該讓線(xiàn)程執(zhí)行wait()方法,阻塞在這里,這不看下ConditionVariable類(lèi)里面這個(gè)函數(shù)

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

這不恰好是我們需要的,我們可以馬上調(diào)用這個(gè)函數(shù)close(),然后讓程序執(zhí)行我們想執(zhí)行的代碼,最后要記得調(diào)用open方法,如下

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

因?yàn)檫@里調(diào)用了notifyAll方法,把之前需要等待的線(xiàn)程呼喚醒

所以我們使用可以這樣使用

1、初始化

ConditionVariable mLock = new ConditionVariable(true);

2、同步的地方這樣使用

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

 
 
四、測(cè)試代碼分析

我先給出一個(gè)原始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;
                }
            }
        }
    }

 

運(yùn)行的結(jié)果是這樣的:

 

    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

很明顯不是我們想要的結(jié)果,因?yàn)槲蚁胍粋€(gè)線(xiàn)程進(jìn)來(lái)了,需要等到執(zhí)行完了才讓另外一個(gè)線(xiàn)程才能進(jìn)來(lái)

 

我們用ConditionVariable來(lái)實(shí)現(xiàn)下

    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();
            }
        }
    }

運(yùn)行的結(jié)果如下

    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

很明顯這是我想要的效果,還有其它辦法嗎?當(dāng)然有

我們還可以使用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();
            }
        }
    }

 
 

運(yùn)行的結(jié)果如下

    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

很明顯這是我想要的效果,還有其它辦法嗎?當(dāng)然有,那就是用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;
                    }
                }
            }
        }
    }

運(yùn)行的結(jié)果如下

    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

很明顯這是我想要的效果

 
 
五、總結(jié)

在Android開(kāi)發(fā)里面我們一般實(shí)現(xiàn)線(xiàn)程通過(guò)可以用ConditionVariable、ReentrantLock(重入鎖)、synchronized、阻塞隊(duì)列(ArrayBlockingQueue、LinkedBlockingQueue)
   put(E e) : 在隊(duì)尾添加一個(gè)元素,如果隊(duì)列滿(mǎn)則阻塞
   size() : 返回隊(duì)列中的元素個(gè)數(shù)
   take() : 移除并返回隊(duì)頭元素,如果隊(duì)列空則阻塞

 


 


 



  作者:chen.yu
深信服三年半工作經(jīng)驗(yàn),目前就職游戲廠商,希望能和大家交流和學(xué)習(xí),
微信公眾號(hào):編程入門(mén)到禿頭 或掃描下面二維碼
零基礎(chǔ)入門(mén)進(jìn)階人工智能(鏈接)