步步爲營_Android開發課[10]_Thread學習

Focus on technology, enjoy life!—— QQ:804212028
瀏覽鏈接:http://blog.csdn.net/y18334702058/article/details/44624305


  • 主題:Thread學習
    -當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如:用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。

線程與進程的關係:

線程是進程中的實體,一個進程可以擁有多個線程,一個線程必須有一個父進程。

創建線程的兩種方式:

1.擴展java.lang.Thread類
擴展java.lang.Thread類,也就是把run()方法寫到線程裏面:

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity {

    private TextView textView;      
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView)findViewById(R.id.textView);

        new Thread(new Runnable()  
        {  
            @Override  
            public void run()  
            {  
                Message message=new Message();  
                message.what=1;  
                handler.sendMessage(message);  
            }  
        }).start();          
    }

    Handler handler = new Handler(){
        public void handleMessage(Message msg){
            switch(msg.what){
            case 1:
                textView.setText("嘟嘟");
                break;
            }
            super.handleMessage(msg);
        }
    };
}

2.實現Runnable接口
讓類實現Runnable接口,讓run方法獨立出來

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

public class MainActivity extends Activity implements Runnable {

    private TextView textView;

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

       textView = (TextView)findViewById(R.id.textView);

       Thread thread = new Thread();
       thread.start();

    }

    Handler mHandler = new Handler(){

        public void handleMessage(Message msg){
            switch(msg.what){
            case 1:
                textView.setText("嘟嘟");
                break;
            }
            super.handleMessage(msg);
        }
    };

    @Override
    public void run() {
        // TODO Auto-generated method stub
        Message msg = new Message();
        msg.what = 1;
        mHandler.sendMessage(msg);
    }
}

經典Thread賣票實例:(能讓我們更加理解線程的使用)

多個窗口一起賣火車票問題。假設有3個窗口同時售票,共有10張火車票代售。啓動三個線程賣共同的10張票。

1.我們開三個線程來賣票:

public class CommonTestActivity extends Activity{

    private Button mybutton;
    private TextView mytext;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mybutton = (Button) findViewById(R.id.button);
        mytext = (TextView) findViewById(R.id.text);

        mybutton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                MyThread myThread1 = new MyThread(); 
                MyThread myThread2= new MyThread(); 
                MyThread myThread3 = new MyThread(); 
                myThread1.start(); 
                myThread2.start(); 
                myThread3.start(); 
            }
        });

    }

    class MyThread extends Thread {
        private int tickets = 10;

        public void run() {
            for (int i = 0; i < 200; i++) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "==>"
                            + tickets--);
                }
            }
        }
    }

}

運行結果:

11-17 22:45:01.234: I/System.out(672): Thread-10==>10
11-17 22:45:01.234: I/System.out(672): Thread-10==>9
11-17 22:45:01.234: I/System.out(672): Thread-10==>8
11-17 22:45:01.234: I/System.out(672): Thread-10==>7
11-17 22:45:01.234: I/System.out(672): Thread-10==>6
11-17 22:45:01.234: I/System.out(672): Thread-10==>5
11-17 22:45:01.234: I/System.out(672): Thread-10==>4
11-17 22:45:01.234: I/System.out(672): Thread-10==>3
11-17 22:45:01.244: I/System.out(672): Thread-10==>2
11-17 22:45:01.244: I/System.out(672): Thread-10==>1
11-17 22:45:01.244: I/System.out(672): Thread-11==>10
11-17 22:45:01.244: I/System.out(672): Thread-11==>9
11-17 22:45:01.244: I/System.out(672): Thread-11==>8
11-17 22:45:01.244: I/System.out(672): Thread-11==>7
11-17 22:45:01.244: I/System.out(672): Thread-11==>6
11-17 22:45:01.254: I/System.out(672): Thread-11==>5
11-17 22:45:01.254: I/System.out(672): Thread-11==>4
11-17 22:45:01.254: I/System.out(672): Thread-11==>3
11-17 22:45:01.254: I/System.out(672): Thread-11==>2
11-17 22:45:01.254: I/System.out(672): Thread-11==>1
11-17 22:45:01.264: I/System.out(672): Thread-12==>10
11-17 22:45:01.264: I/System.out(672): Thread-12==>9
11-17 22:45:01.264: I/System.out(672): Thread-12==>8
11-17 22:45:01.264: I/System.out(672): Thread-12==>7
11-17 22:45:01.264: I/System.out(672): Thread-12==>6
11-17 22:45:01.274: I/System.out(672): Thread-12==>5
11-17 22:45:01.274: I/System.out(672): Thread-12==>4
11-17 22:45:01.274: I/System.out(672): Thread-12==>3
11-17 22:45:01.274: I/System.out(672): Thread-12==>2
11-17 22:45:01.274: I/System.out(672): Thread-12==>1

分析:
運行結果與預期不一致,分析可以看出3個線程各種賣各自的10張票,而不是共同的10張票。

2.只修改onClick裏面的代碼,並分析運行結果

public void onClick(View v) { 
    //實例化線程對象 
    MyThread myThread = new MyThread(); 
    new Thread(myThread, "窗口1").start(); 
    new Thread(myThread, "窗口2").start(); 
    new Thread(myThread, "窗口3").start(); 
}

運行結果:

11-17 22:49:17.314: I/System.out(708): 窗口3==>10
11-17 22:49:17.314: I/System.out(708): 窗口3==>9
11-17 22:49:17.314: I/System.out(708): 窗口3==>8
11-17 22:49:17.314: I/System.out(708): 窗口3==>7
11-17 22:49:17.314: I/System.out(708): 窗口3==>6
11-17 22:49:17.324: I/System.out(708): 窗口3==>5
11-17 22:49:17.324: I/System.out(708): 窗口3==>4
11-17 22:49:17.324: I/System.out(708): 窗口3==>3
11-17 22:49:17.324: I/System.out(708): 窗口3==>2
11-17 22:49:17.324: I/System.out(708): 窗口3==>1

分析:
此處3個窗口本來是已經在賣共同的10張票了,只不過由於沒有進行線程同步,會隨機選定一個線程窗口來執行賣票直到賣完,致使其他窗口沒機會賣。

3.下面繼續修改代碼,驗證我剛得猜測。

public void onClick(View v) { 
    //實例化線程對象 
    MyThread myThread = new MyThread(); 
    new Thread(myThread, "窗口1").start(); 
    new Thread(myThread, "窗口2").start(); 
    new Thread(myThread, "窗口3").start(); 
}
class MyThread extends Thread{ 
    private int tickets = 10; 
    public void run() { 
        for(int i = 0; i< 200; i++){ 
                sell(); 
        } 
    } 
    public synchronized  void sell(){ 
            if(tickets > 0) 
            { 
                System.out.println(Thread.currentThread().getName() + "==>" + tickets--); 
            } 
    } 
}

運行結果:

05-11 08:53:31.986: INFO/System.out(7116): 窗口1==>10
05-11 08:53:32.006: INFO/System.out(7116): 窗口1==>9
05-11 08:53:32.066: INFO/System.out(7116): 窗口1==>7
05-11 08:53:32.086: INFO/System.out(7116): 窗口1==>6
05-11 08:53:32.106: INFO/System.out(7116): 窗口1==>5
05-11 08:53:32.106: INFO/System.out(7116): 窗口1==>4
05-11 08:53:32.126: INFO/System.out(7116): 窗口1==>3
05-11 08:53:32.146: INFO/System.out(7116): 窗口1==>2
05-11 08:53:32.146: INFO/System.out(7116): 窗口1==>1

分析:
一個對象只有一個鎖。所以,如果一個線程獲得該鎖,就沒有其他線程可以獲得鎖,直到第這個線程釋放(或返回)鎖。這也意味着任何其他線程都不能進入該對象上的synchronized方法或代碼塊,直到該鎖被釋放。
釋放鎖是指持鎖線程退出了synchronized同步方法或代碼塊。
上述代碼沒有Thread.sleep(10),換句話說線程一一直處於運行狀態,沒有釋放鎖,沒有給其他線程運行的機會。

4.根據上述分析,修改代碼如下:

public void onClick(View v) { 
        //實例化線程對象 
        MyThread myThread = new MyThread(); 
        new Thread(myThread, "窗口1").start(); 
        new Thread(myThread, "窗口2").start(); 
        new Thread(myThread, "窗口3").start(); 
    } 
    class MyThread extends Thread{ 
        private int tickets = 10; 
        public void run() { 
            for(int i = 0; i< 200; i++){ 
                try{ 
                    sell(); 
                    Thread.sleep(10); 
                }catch (InterruptedException e) { 
                    System.out.println("我被打斷了" + Thread.currentThread().getName()); 
                    e.printStackTrace(); 
                }
            } 
        } 
        public synchronized  void sell(){ 
                if(tickets > 0) 
                { 
                    System.out.println(Thread.currentThread().getName() + "==>" + tickets--); 
                } 
        } 
    }

運行結果:

05-11 09:17:07.496: INFO/System.out(7898): 窗口1==>10
05-11 09:17:07.528: INFO/System.out(7898): 窗口1==>9
05-11 09:17:07.546: INFO/System.out(7898): 窗口1==>8
05-11 09:17:07.577: INFO/System.out(7898): 窗口1==>7
05-11 09:17:07.626: INFO/System.out(7898): 窗口3==>6
05-11 09:17:07.626: INFO/System.out(7898): 窗口2==>5
05-11 09:17:07.636: INFO/System.out(7898): 窗口1==>4
05-11 09:17:07.646: INFO/System.out(7898): 窗口2==>3
05-11 09:17:07.646: INFO/System.out(7898): 窗口1==>2
05-11 09:17:07.656: INFO/System.out(7898): 窗口3==>1

分析:
此次得出的正是我們想要的結果,現在知道火車站是怎麼賣票的啦。

//有的同學可能還不太理解上面程序中synchronized的用法,那我們繼續往下看

synchronized 主要用法

1.方法聲明時使用,放在範圍操作符(public等)之後,返回類型聲明(void等)之前。即一次只能有一個線程進入該方法,其他線程要想在此時調用該方法,只能排隊等候,當前線程(就是在synchronized方法內部的線程)執行完該方法後,別的線程才能進入。
例如:

public synchronized void synMethod() {
//方法體
}

2.對某一代碼塊使用,synchronized後跟括號,括號裏是變量,這樣,一次只有一個線程進入該代碼塊。例如:

public int synMethod(int a1){
synchronized(a1) {
//一次只能有一個線程進入

Focus on technology, enjoy life!—— QQ:804212028
瀏覽鏈接:http://blog.csdn.net/y18334702058/article/details/44624305

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