Java 線程調度我就寫一篇(Object與Semaphore、Lock、Callable、Future、FutureTask)

一.Object

一般來說Synchronized 同步鎖就能解決大部分線程同步問題,但是如果情況比較複雜就不行了,這就得用Object的wait(),notify(),notifyAll()。但是這個三個函數只能實現不公平鎖,Semaphore能夠實現公平鎖和不公平鎖,後面再說。

概念

Synchronized:同步鎖,指的是他能鎖住對象、類、函數、代碼塊,使得同一時間這些東西只能被一個線程佔用。
Object.wait():表示進入等待狀態,使得當前線程當前代碼段放棄Object的控制權(ps:sleep()不釋放同步鎖,wait()釋放同步縮.  )
Object.notify():單一通知,就是讓隨機一個處於等待狀態的Object所處代碼段所處線程重新獲得這個Object的控制權。
Object.notifyAll():使得所有處於等待狀態的Object的所處代碼段所處線程重新獲得這個Object的控制權。
 

注意:

這個Object的引用地址不能改變,Object引用的對象的內部參數可以改變。

然後使用這個三個函數的時候必須加上synchronized的同步鎖,保持同步,

在線程A,一個對象在自己的同步鎖代碼塊裏執行了wait,然後這個對象在線程B執行了notify通知到線程A繼續運行,這個時候原來的線程不是繼續運行,而是重新把同步鎖代碼塊從頭到尾運行一遍。而且Object的wait函數能夠停止當前線程的運行,但是不能實現一個代碼段在同一時間只有一個線程執行,所以需要synchronized 來完成這個任務。

 


這個時候我們再構建一個生產者和消費者的關係,就是生產者生產產品給消費者消費,如果生產者的生產速度高於消費者的消費速度,那沒問題,如果低於消費者的消費速度。消費者在得知沒有產品了,他就只能等待,然後等到生產者生產出產品通知消費者,消費者再來消費。


其實這個關係我之前在NDK的pthread裏說過,
http://blog.csdn.net/z979451341/article/details/79423618

 

 

 

因爲這個三個函數都是調用底層c語言弄的,所以我找不到真正的源碼,但是我可以告訴你和NDK的pthread沒得跑

 

 

    @FastNative
    public final native void notify();
    @FastNative
    public final native void notifyAll();
    @FastNative
    public final native void wait() throws InterruptedException;

 

好了,我們先上例子代碼,再來說說生產者和消費者的關係,和這個三個函數的使用情況

生產者

 

public class ProductThread extends Thread {

    private DataBean dataBean;
    private boolean state = true;

    public void setState(boolean state){
        this.state = state;
    }

    public ProductThread(String name, DataBean dataBean){
        super(name);
        this.dataBean = dataBean;
    }

    @Override
    public void run(){

        for(;state;){
            try{
                sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }


            synchronized (dataBean){
                dataBean.setId(dataBean.getId()+1);
                Log.v("zzw","product 1, now "+dataBean.getId());
                dataBean.notify();
            }
        }
    }
    
}

 

消費者

 

public class ConsumerThread extends Thread{
    private DataBean dataBean;

    private boolean state = true;

    public void setState(boolean state){
        this.state = state;
    }

    public ConsumerThread(String name, DataBean dataBean){
        super(name);
        this.dataBean = dataBean;
    }

    public void run(){
        for(;state;){
            try{
                sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            synchronized (dataBean){
                if(dataBean.getId() == 0){
                    Log.v("zzw",getName()+" wait");
                    try {
                        dataBean.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }else {
                    dataBean.setId(dataBean.getId() - 1);
                    Log.v("zzw",getName()+" consumer 1,  now "+dataBean.getId());
                }
            }

        }

    }
}

 

主函數

 

public class FourActivity extends AppCompatActivity {

    private Button btn;

    DataBean dataBean;
    private ConsumerThread consumer_one,consumer_two,consumer_three;
    private ProductThread productThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_four);

        dataBean = new DataBean();
        dataBean.setId(0);
        productThread = new ProductThread("product",dataBean);


        consumer_one = new ConsumerThread("one",dataBean);
        consumer_two = new ConsumerThread("two",dataBean);
        consumer_three = new ConsumerThread("three",dataBean);

        productThread.start();
        consumer_one.start();
        consumer_two.start();
        consumer_three.start();

        btn = (Button)findViewById(R.id.btn_one);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                productThread.setState(false);
                consumer_one.setState(false);
                consumer_two.setState(false);
                consumer_three.setState(false);

            }
        });
    }
}

 

我們看的出來生產者有一個,消費者有三個

生產速度小於消費速度

 

03-17 09:11:37.842 15782-15883/com.example.zth.seven V/zzw: product 1, now 1
03-17 09:11:37.844 15782-15884/com.example.zth.seven V/zzw: one consumer 1,  now 0
03-17 09:11:37.844 15782-15885/com.example.zth.seven V/zzw: two wait
03-17 09:11:37.844 15782-15886/com.example.zth.seven V/zzw: three wait
03-17 09:11:38.842 15782-15883/com.example.zth.seven V/zzw: product 1, now 1
03-17 09:11:38.844 15782-15884/com.example.zth.seven V/zzw: one consumer 1,  now 0
03-17 09:11:39.843 15782-15883/com.example.zth.seven V/zzw: product 1, now 1
03-17 09:11:39.843 15782-15885/com.example.zth.seven V/zzw: two consumer 1,  now 0
03-17 09:11:39.844 15782-15884/com.example.zth.seven V/zzw: one wait

 

從log看得出第一輪值滿足了一個消費者,另外兩個等待,後面繼續生產隨機通知一個
當生產速度等於消費速度,並且讓我們試一下notify與notifyAll的使用區別

修改一些生產者的代碼

 

            synchronized (dataBean){
                dataBean.setId(dataBean.getId()+3);
                Log.v("zzw","product 3, now "+dataBean.getId());
                dataBean.notify();
            }

 

結果

 

03-17 09:38:24.654 20465-20555/com.example.zth.seven V/zzw: product 3, now 3
03-17 09:38:24.654 20465-20556/com.example.zth.seven V/zzw: one consumer 1,  now 2
03-17 09:38:24.654 20465-20557/com.example.zth.seven V/zzw: two consumer 1,  now 1
03-17 09:38:24.654 20465-20558/com.example.zth.seven V/zzw: three consumer 1,  now 0
03-17 09:38:25.655 20465-20556/com.example.zth.seven V/zzw: one wait
03-17 09:38:25.655 20465-20555/com.example.zth.seven V/zzw: product 3, now 3
03-17 09:38:25.655 20465-20558/com.example.zth.seven V/zzw: three consumer 1,  now 2
03-17 09:38:25.655 20465-20557/com.example.zth.seven V/zzw: two consumer 1,  now 1
03-17 09:38:26.655 20465-20555/com.example.zth.seven V/zzw: product 3, now 4
03-17 09:38:26.655 20465-20558/com.example.zth.seven V/zzw: three consumer 1,  now 3
03-17 09:38:26.655 20465-20557/com.example.zth.seven V/zzw: two consumer 1,  now 2
03-17 09:38:26.655 20465-20556/com.example.zth.seven V/zzw: one consumer 1,  now 1

 

一開始三個消費者都可以去消費,就是第一個消費者因爲搶在生產者生產之前就開始了第二輪消費,所以他就進入等待狀態,因爲第二輪生產沒有隨機通知到one,所以就剩下一個產品導致後面消費者一直都在消費沒有進入等待狀態。

修改生產者代碼用notifyAll

 

            synchronized (dataBean){
                dataBean.setId(dataBean.getId()+3);
                Log.v("zzw","product 3, now "+dataBean.getId());
                dataBean.notifyAll();
            }

 

看log

 

03-17 09:47:21.022 22561-22583/com.example.zth.seven V/zzw: one wait
03-17 09:47:21.022 22561-22585/com.example.zth.seven V/zzw: three wait
03-17 09:47:21.022 22561-22584/com.example.zth.seven V/zzw: two wait
03-17 09:47:21.022 22561-22582/com.example.zth.seven V/zzw: product 3, now 3
03-17 09:47:22.023 22561-22583/com.example.zth.seven V/zzw: one consumer 1,  now 2
03-17 09:47:22.023 22561-22584/com.example.zth.seven V/zzw: two consumer 1,  now 1
03-17 09:47:22.023 22561-22585/com.example.zth.seven V/zzw: three consumer 1,  now 0
03-17 09:47:22.023 22561-22582/com.example.zth.seven V/zzw: product 3, now 3
03-17 09:47:23.023 22561-22583/com.example.zth.seven V/zzw: one consumer 1,  now 2
03-17 09:47:23.024 22561-22585/com.example.zth.seven V/zzw: three consumer 1,  now 1
03-17 09:47:23.024 22561-22584/com.example.zth.seven V/zzw: two consumer 1,  now 0
03-17 09:47:23.024 22561-22582/com.example.zth.seven V/zzw: product 3, now 3
03-17 09:47:24.024 22561-22583/com.example.zth.seven V/zzw: one consumer 1,  now 2
03-17 09:47:24.024 22561-22585/com.example.zth.seven V/zzw: three consumer 1,  now 1

 

開始三個消費者先一步開始消費,因爲這個時候生產者沒有生成就進入等待,這個時候得提一句,之前基本都是第一輪生成者先生成的,因爲是生產者的線程最先開啓,但是這一回不同,因爲notifyAll比notify要耗時很多,畢竟是通知所有等待。然後後面因爲生成速度跟得上消費速度,所以消費者一直消費沒有進入消費狀態。這個時候我在想如果使用三個notify是不是比這一個notifyAll要好。

修改生成者代碼

 

                dataBean.setId(dataBean.getId()+3);
                Log.v("zzw","product 3, now "+dataBean.getId());
                dataBean.notify();
                dataBean.notify();
                dataBean.notify();

 

結果

 

03-17 09:54:40.775 23718-23785/com.example.zth.seven V/zzw: product 3, now 3
03-17 09:54:40.777 23718-23787/com.example.zth.seven V/zzw: two consumer 1,  now 2
03-17 09:54:40.778 23718-23788/com.example.zth.seven V/zzw: three consumer 1,  now 1
03-17 09:54:40.778 23718-23786/com.example.zth.seven V/zzw: one consumer 1,  now 0
03-17 09:54:41.776 23718-23785/com.example.zth.seven V/zzw: product 3, now 3
03-17 09:54:41.778 23718-23787/com.example.zth.seven V/zzw: two consumer 1,  now 2
03-17 09:54:41.778 23718-23788/com.example.zth.seven V/zzw: three consumer 1,  now 1
03-17 09:54:41.778 23718-23786/com.example.zth.seven V/zzw: one consumer 1,  now 0
03-17 09:54:42.777 23718-23785/com.example.zth.seven V/zzw: product 3, now 3
03-17 09:54:42.778 23718-23787/com.example.zth.seven V/zzw: two consumer 1,  now 2
03-17 09:54:42.779 23718-23788/com.example.zth.seven V/zzw: three consumer 1,  now 1
03-17 09:54:42.779 23718-23786/com.example.zth.seven V/zzw: one consumer 1,  now 0

 

果然三個notify比notifyAll要好


結論:notifyAll很耗時,可以用幾個notify來代替,當然線程數量多了不敢保證,然後就是notify或者notifyAll通知wait,會讓Object所處線程重新運行
 

 

還有幾個生成者對應幾個消費者的情況,因爲得到的結論一樣,大家只用跟着再創建幾個生成者線程,並放入同一個Object就可以了,然後你們自己玩耍

 

附加一個阿里面試題的解法

題目:多線程,5個線程內部打印hello和world,hello在前,要求提供一種方法使得5個線程先全部打印出hello後再打印5個word。

答案:說白了就是通過一個實體類記錄幾個線程執行打印hello,在打印word之前判斷目前是否有五個線程都打印了hello,如果不是就調用wait等待,如果是的就調用notifyAll通知所有線程,重新執行同步鎖的代碼,此時滿足條件繼續執行代碼,打印world

public class TestThread extends Thread {

    TestBean bean;

    public TestThread(TestBean bean) {
        super();
        this.bean = bean;
    }

    @Override
    public void run() {
        super.run();
        Log.v("zzw",Thread.currentThread()+"hello");
        bean.setNum(bean.getNum()+1);
        synchronized (bean) {
            if (bean.getNum() >= 5) {
                bean.notifyAll();
            } else {

                try {
                    bean.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        Log.v("zzw",Thread.currentThread()+"world");

    }
}

 

public class TestBean {

    private int num;

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

 

 

 

public class FourActivity extends AppCompatActivity {

    TestThread one, two, three, four, five;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_four);

        TestBean bean = new TestBean();
        bean.setNum(0);
        one = new TestThread(bean);
        two = new TestThread(bean);
        three = new TestThread(bean);
        four = new TestThread(bean);
        five = new TestThread(bean);

        one.start();
        two.start();
        three.start();
        four.start();
        five.start();



    }





}

 

 

二.semaphore

Semaphore可以根據情況來讓一個代碼段在同一時間只有一個線程執行,但是不能直接執行停止線程。

 

Semaphore,計數信號量,之所以要說這,因爲之前的Object三個函數只實現了不公平的線程同步(隨機讓一個等待線程開啓),而Semaphore能夠實現公平的線程同步(讓等待時間最長的線程開啓),而且不用synchronize來實現線程堵塞。

Semaphore是通過控制類似許可證的數量來完成控制線程數量。

初始化Semaphore,規定正在運行的線程數量,這是規定了3爲最大許可證數量(

Semaphore semaphore = new Semaphore(3);

上面創建的是公平模式,還有不公平模式

    public Semaphore(int permits, boolean fair) 

 

當我們運行線程的run函數時,請求許可證,如果當前運行的線程數量比最大線程數少,他就會允許這個線程運行,同時計算已用許可證的變量加一,否則堵塞線程。

 

semaphore.acquire();

還有就是這個acquire函數可以傳入整數,也就是一個線程可以獲取幾個許可證,不過如果最大許可證數量爲10,你執行acquire(3),那個線程最多三個同時運行,畢竟10/3=3

semaphore.acquire(3);

當run函數運行完畢,釋放許可證,已用許可證的變量建議

semaphore.release();

減少當前可用許可證的數量,也就是增加已用許可證的數量

protected void reducePermits(int reduction)

獲取剩餘許可證的數量

public int drainPermits()

我首先演示一下使用Semaphore的過程

首先編寫線程代碼,其中線程構造函數傳入Semaphore對象並給線程取名字,便於後期識別

public class TestThread extends Thread{

    Semaphore semaphore;
    TestThread (String name,Semaphore semaphore) {
        super(name);
        this.semaphore = semaphore;

    }
    @Override
    public void run() {

        try {
            // 從信號量中獲取一個允許機會
            semaphore.acquire();
            Log.v("zzw",Thread.currentThread().getName() + " start at " + System.currentTimeMillis());
            Thread.sleep(1000);
            Log.v("zzw",Thread.currentThread().getName() + " stop at " + System.currentTimeMillis());
            // 釋放允許,將佔有的信號量歸還
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

開啓10個線程,設置一個最大許可證數爲3的Semaphore

public class OneActivity extends Activity {

    Semaphore semaphore = new Semaphore(3);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.one);
        Button btn_one = (Button) findViewById(R.id.btn_one);
        btn_one.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                for (int i = 0; i < 10; i++) {
                    new TestThread(i+"",semaphore).start();
                }

            }
        });
    }
}

結果,開啓的10個線程,都是按照開啓順序獲取許可證取運行的

05-13 16:22:01.363 5038-5146/zzw.myapplication V/zzw: 1 start at 1526199721363
05-13 16:22:01.364 5038-5149/zzw.myapplication V/zzw: 2 start at 1526199721364
05-13 16:22:01.364 5038-5145/zzw.myapplication V/zzw: 0 start at 1526199721364
05-13 16:22:02.363 5038-5146/zzw.myapplication V/zzw: 1 stop at 1526199722363
05-13 16:22:02.364 5038-5150/zzw.myapplication V/zzw: 3 start at 1526199722364
05-13 16:22:02.364 5038-5149/zzw.myapplication V/zzw: 2 stop at 1526199722364
05-13 16:22:02.364 5038-5145/zzw.myapplication V/zzw: 0 stop at 1526199722364
05-13 16:22:02.364 5038-5151/zzw.myapplication V/zzw: 4 start at 1526199722364
05-13 16:22:02.365 5038-5152/zzw.myapplication V/zzw: 5 start at 1526199722365
05-13 16:22:03.364 5038-5150/zzw.myapplication V/zzw: 3 stop at 1526199723364
05-13 16:22:03.364 5038-5153/zzw.myapplication V/zzw: 6 start at 1526199723364
05-13 16:22:03.365 5038-5151/zzw.myapplication V/zzw: 4 stop at 1526199723365
05-13 16:22:03.365 5038-5152/zzw.myapplication V/zzw: 5 stop at 1526199723365
05-13 16:22:03.365 5038-5154/zzw.myapplication V/zzw: 7 start at 1526199723365
05-13 16:22:03.366 5038-5155/zzw.myapplication V/zzw: 8 start at 1526199723366
05-13 16:22:04.365 5038-5153/zzw.myapplication V/zzw: 6 stop at 1526199724364
05-13 16:22:04.365 5038-5156/zzw.myapplication V/zzw: 9 start at 1526199724365
05-13 16:22:04.366 5038-5154/zzw.myapplication V/zzw: 7 stop at 1526199724366
05-13 16:22:04.366 5038-5155/zzw.myapplication V/zzw: 8 stop at 1526199724366
05-13 16:22:05.366 5038-5156/zzw.myapplication V/zzw: 9 stop at 1526199725366

如果我們把Semaphore改成非公平模式

Semaphore semaphore = new Semaphore(3,false);

 

結果看起來還是比較有序,畢竟創建和開啓線程需要一定的時間,但是這裏可以看得到線程6明顯排在後頭了

 

05-13 16:54:00.585 12612-13069/zzw.myapplication V/zzw: 2 start at 1526201640584
05-13 16:54:00.585 12612-13068/zzw.myapplication V/zzw: 1 start at 1526201640585
05-13 16:54:00.585 12612-13066/zzw.myapplication V/zzw: 0 start at 1526201640585
05-13 16:54:01.586 12612-13069/zzw.myapplication V/zzw: 2 stop at 1526201641586
05-13 16:54:01.586 12612-13068/zzw.myapplication V/zzw: 1 stop at 1526201641585
05-13 16:54:01.586 12612-13066/zzw.myapplication V/zzw: 0 stop at 1526201641586
05-13 16:54:01.586 12612-13070/zzw.myapplication V/zzw: 3 start at 1526201641586
05-13 16:54:01.586 12612-13071/zzw.myapplication V/zzw: 4 start at 1526201641586
05-13 16:54:01.587 12612-13072/zzw.myapplication V/zzw: 5 start at 1526201641587
05-13 16:54:02.586 12612-13070/zzw.myapplication V/zzw: 3 stop at 1526201642586
05-13 16:54:02.586 12612-13071/zzw.myapplication V/zzw: 4 stop at 1526201642586
05-13 16:54:02.587 12612-13074/zzw.myapplication V/zzw: 7 start at 1526201642587
05-13 16:54:02.588 12612-13072/zzw.myapplication V/zzw: 5 stop at 1526201642588
05-13 16:54:02.588 12612-13075/zzw.myapplication V/zzw: 8 start at 1526201642588
05-13 16:54:02.589 12612-13073/zzw.myapplication V/zzw: 6 start at 1526201642589
05-13 16:54:03.588 12612-13074/zzw.myapplication V/zzw: 7 stop at 1526201643588
05-13 16:54:03.589 12612-13076/zzw.myapplication V/zzw: 9 start at 1526201643589
05-13 16:54:03.589 12612-13075/zzw.myapplication V/zzw: 8 stop at 1526201643589
05-13 16:54:03.589 12612-13073/zzw.myapplication V/zzw: 6 stop at 1526201643589
05-13 16:54:04.589 12612-13076/zzw.myapplication V/zzw: 9 stop at 1526201644589

 

3.ReentrantLock

Lock既可以保證代碼段在同一時間只有一個線程執行,並且堵塞線程和釋放線程。

Lock的lock函數和unlock可以完成 代碼段在在同一時間只有一個線程執行(RentrantLock必須是同一個對象)

我這裏直接上代碼和其log結果

public class MyThread extends Thread {

    ReentrantLock lock;
    MyThread(ReentrantLock lock, String name){
        super(name);
        this.lock=lock;
    }

    @Override
    public void run() {
        super.run();

        lock.lock();


        for(int i=0;i<5;i++){
            Log.v("zzw",getName()+" "+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


        lock.unlock();

    }
}
        ReentrantLock lock=new ReentrantLock();
        Condition condition=lock.newCondition();
        for(int i=0;i<5;i++){
            MyThread one=new MyThread(lock,""+i);
            one.start();

        }
10-15 09:22:39.618 9097-9152/? V/zzw: 1 0
10-15 09:22:40.619 9097-9152/bitpai.com.two V/zzw: 1 1
10-15 09:22:41.620 9097-9152/bitpai.com.two V/zzw: 1 2
10-15 09:22:42.621 9097-9152/bitpai.com.two V/zzw: 1 3
10-15 09:22:43.622 9097-9152/bitpai.com.two V/zzw: 1 4
10-15 09:22:44.623 9097-9154/bitpai.com.two V/zzw: 3 0
10-15 09:22:45.625 9097-9154/bitpai.com.two V/zzw: 3 1
10-15 09:22:46.626 9097-9154/bitpai.com.two V/zzw: 3 2
10-15 09:22:47.626 9097-9154/bitpai.com.two V/zzw: 3 3
10-15 09:22:48.627 9097-9154/bitpai.com.two V/zzw: 3 4
10-15 09:22:49.629 9097-9151/bitpai.com.two V/zzw: 0 0
10-15 09:22:50.631 9097-9151/bitpai.com.two V/zzw: 0 1
10-15 09:22:51.632 9097-9151/bitpai.com.two V/zzw: 0 2
10-15 09:22:52.633 9097-9151/bitpai.com.two V/zzw: 0 3
10-15 09:22:53.634 9097-9151/bitpai.com.two V/zzw: 0 4
10-15 09:22:54.636 9097-9153/bitpai.com.two V/zzw: 2 0
10-15 09:22:55.637 9097-9153/bitpai.com.two V/zzw: 2 1
10-15 09:22:56.638 9097-9153/bitpai.com.two V/zzw: 2 2
10-15 09:22:57.639 9097-9153/bitpai.com.two V/zzw: 2 3
10-15 09:22:58.639 9097-9153/bitpai.com.two V/zzw: 2 4
10-15 09:22:59.641 9097-9155/bitpai.com.two V/zzw: 4 0
10-15 09:23:00.642 9097-9155/bitpai.com.two V/zzw: 4 1
10-15 09:23:01.643 9097-9155/bitpai.com.two V/zzw: 4 2
10-15 09:23:02.644 9097-9155/bitpai.com.two V/zzw: 4 3
10-15 09:23:03.645 9097-9155/bitpai.com.two V/zzw: 4 4

如果給每一個Thread不同的ReentrantLock對象

        for(int i=0;i<5;i++){
            Log.v("zzw",getName()+" "+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

log如下,各個線程的運行互不影響

10-17 18:47:13.042 10650-10703/? V/zzw: 3 0
10-17 18:47:13.042 10650-10704/? V/zzw: 4 0
10-17 18:47:13.042 10650-10701/? V/zzw: 1 0
10-17 18:47:13.043 10650-10702/? V/zzw: 2 0
10-17 18:47:13.043 10650-10700/? V/zzw: 0 0
10-17 18:47:14.043 10650-10701/bitpai.com.two V/zzw: 1 1
10-17 18:47:14.043 10650-10704/bitpai.com.two V/zzw: 4 1
10-17 18:47:14.043 10650-10703/bitpai.com.two V/zzw: 3 1
10-17 18:47:14.043 10650-10700/bitpai.com.two V/zzw: 0 1
10-17 18:47:14.044 10650-10702/bitpai.com.two V/zzw: 2 1
10-17 18:47:15.044 10650-10703/bitpai.com.two V/zzw: 3 2
10-17 18:47:15.044 10650-10704/bitpai.com.two V/zzw: 4 2
10-17 18:47:15.044 10650-10702/bitpai.com.two V/zzw: 2 2
10-17 18:47:15.044 10650-10700/bitpai.com.two V/zzw: 0 2
10-17 18:47:15.044 10650-10701/bitpai.com.two V/zzw: 1 2
10-17 18:47:16.044 10650-10703/bitpai.com.two V/zzw: 3 3
10-17 18:47:16.045 10650-10700/bitpai.com.two V/zzw: 0 3
10-17 18:47:16.045 10650-10702/bitpai.com.two V/zzw: 2 3
10-17 18:47:16.045 10650-10704/bitpai.com.two V/zzw: 4 3
10-17 18:47:16.045 10650-10701/bitpai.com.two V/zzw: 1 3
10-17 18:47:17.045 10650-10703/bitpai.com.two V/zzw: 3 4
10-17 18:47:17.045 10650-10700/bitpai.com.two V/zzw: 0 4
10-17 18:47:17.045 10650-10704/bitpai.com.two V/zzw: 4 4
10-17 18:47:17.045 10650-10701/bitpai.com.two V/zzw: 1 4
10-17 18:47:17.045 10650-10702/bitpai.com.two V/zzw: 2 4

 

Lock可以創建Condition對象,以此來完成線程的堵塞和釋放,這個Condition與之前說的Object具有同樣的能力

還是繼續貼代碼和log結果

public class MyThread extends Thread {

    ReentrantLock lock;
    Condition condition;
    MyThread(ReentrantLock lock, String name,Condition condition){
        super(name);
        this.lock=lock;
        this.condition=condition;
    }

    @Override
    public void run() {
        super.run();

        lock.lock();


        for(int i=0;i<5;i++){
            Log.v("zzw",getName()+" "+i);
            if(i==2){
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


        lock.unlock();

    }

    public void release(){
        lock.lock();
        condition.signal();//需要注意condition使用的時候前後一定要 Lock上鎖和解鎖
        lock.unlock();
    }
}
        ReentrantLock lock=new ReentrantLock();
        Condition condition=lock.newCondition();
        final MyThread one=new MyThread(lock,"one",condition);
        one.start();

        Button btn=(Button)findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                one.release();
            }
        });

在打印出2的時候停住了,然後再點擊一次按鈕,繼續彈出3、4

10-15 09:37:23.128 12880-12927/bitpai.com.two V/zzw: one 0
10-15 09:37:24.129 12880-12927/bitpai.com.two V/zzw: one 1
10-15 09:37:25.131 12880-12927/bitpai.com.two V/zzw: one 2  
10-15 09:37:32.094 12880-12927/bitpai.com.two V/zzw: one 3
10-15 09:37:33.094 12880-12927/bitpai.com.two V/zzw: one 4

 

4.Callable、Future和FutureTask

這三個主要是爲了方便返回子線程的結果,但是對於什麼是否返回結果不能對齊進行監控

Callable:一個擁有返回值的Runnable。

Future和FutureTask:用於獲取Callable的運行狀態,比如是否正在運行,是否運算接受,還用於接受Callable的返回值

 

Future.isDone() :判斷任務是否執行完

Future.isCanlled():判斷任務是否取消了

Future.get():獲取返回值,如果任務還沒執行完,會堵塞當前線程,所以經常需要和isDone()配合使用

Future.cancel():取消執行任務,但是如果任務已經在執行了,就會讓他繼續執行

 

Future配合Callable如下

public class TwoActivity extends AppCompatActivity {

    ExecutorService executorService= Executors.newCachedThreadPool();
    Task task=new Task();
    Future<Integer> result;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_two);

        result=executorService.submit(task);

        ((Button)findViewById(R.id.btn)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(result.isDone()){
                    try {
                        Log.v("zzw","結果爲"+result.get());
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    Log.v("zzw","還在計算");
                }
            }
        });
    }

    class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        }
    }

}

 

FutureTask和Future差不多,只是它直接包裝了Callable

public class TwoActivity extends AppCompatActivity {

    ExecutorService executorService= Executors.newCachedThreadPool();
    Task task=new Task();
    FutureTask<Integer> futureTask=new FutureTask<Integer>(task);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_two);

        executorService.submit(futureTask);
        ((Button)findViewById(R.id.btn)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(futureTask.isDone()){
                    try {

                        Log.v("zzw","結果爲"+futureTask.get());
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    Log.v("zzw","還在計算");
                }
            }
        });
    }

    class Task implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            Thread.sleep(3000);
            int sum = 0;
            for (int i = 0; i < 100; i++)
                sum += i;
            return sum;
        }
    }

}

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章