国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Android開發(fā)中的多線程編程技術(shù)

Android開發(fā)中的多線程編程技術(shù)

http://software.intel.com/zh-cn/articles/android-0/?utm_campaign=CSDN&utm_source=intel.csdn.net&utm_medium=Link&utm_content=%20intelandroid%20%E2%80%93%20duoxiancheng

多線程這個令人生畏的“洪水猛獸”,很多人談起多線程都心存畏懼。在Android開發(fā)過程中,多線程真的很難嗎?多線程程序的“麻煩”源于它很抽象、與單線程程序運(yùn)行模式不同,但只要掌握了它們的區(qū)別,編寫多線程程序就會很容易了。下面讓我們集中精力開始學(xué)習(xí)吧!

  多線程案例——計(jì)時器

  我在給我的學(xué)生講多線程的時候都會舉一個計(jì)時器的案例,因?yàn)橛?jì)時器案例是多線程的經(jīng)典應(yīng)用。

  這個案例中,屏幕啟動之后,進(jìn)入如圖8-1所示的界面。

  屏幕上有一個文本框用于顯示逝去的時間,此外還有一個“停止計(jì)時”按鈕。案例的用例圖如圖8-2所示。

  

  ▲圖8-1 計(jì)時器界面

  

  ▲圖8-2 計(jì)時器用例圖

  能夠在屏幕上“實(shí)時地顯示”時間的流逝,單線程程序是無法實(shí)現(xiàn)的,必須要多線程程序才可以實(shí)現(xiàn),即便有些計(jì)算機(jī)語言可以通過封裝好的類實(shí)現(xiàn)這一功能,但從本質(zhì)上講這些封裝好的類就是封裝了一個線程。

  綜上所述,完成本案例用到的知識及技術(shù)如下:

  1)進(jìn)程和線程的概念;

  2)Java中的線程,在Java中創(chuàng)建線程的方式;

  3)Android中的線程,包括:Message、Handler、Looper和HandlerThread等概念。

  線程究竟是什么?在Windows操作系統(tǒng)出現(xiàn)之前,個人計(jì)算機(jī)上的操作系統(tǒng)都是單任務(wù)系統(tǒng),只有在大型計(jì)算機(jī)上才具有多任務(wù)和分時設(shè)計(jì)。Windows、Linux操作系統(tǒng)的出現(xiàn),把原本只在大型計(jì)算機(jī)才具有的優(yōu)點(diǎn),帶到了個人計(jì)算機(jī)系統(tǒng)中。

  進(jìn)程概念

  一般可以在同一時間內(nèi)執(zhí)行多個程序的操作系統(tǒng)都有進(jìn)程的概念。一個進(jìn)程就是一個執(zhí)行中的程序,而每一個進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間、一組系統(tǒng)資源。在進(jìn)程的概念中,每一個進(jìn)程的內(nèi)部數(shù)據(jù)和狀態(tài)都是完全獨(dú)立的。在Windows操作系統(tǒng)下我們可以通過〈Ctrl+Alt+Del〉組合鍵查看進(jìn)程,在UNIX和Linux操作系統(tǒng)下是通過PS命令查看進(jìn)程的。打開Windows當(dāng)前運(yùn)行的進(jìn)程,如圖8-3所示。

  

  ▲圖8-3 Windows操作系統(tǒng)進(jìn)程

  在Windows操作系統(tǒng)中一個進(jìn)程就是一個exe或dll程序,它們相互獨(dú)立,互相也可以通信,在Android操作系統(tǒng)中進(jìn)程間的通信應(yīng)用也是很多的。

  線程概念

  多線程指的是在單個程序中可以同時運(yùn)行多個不同的線程,執(zhí)行不同的任務(wù)。多線程意味著一個程序的多行語句可以看上去幾乎在同一時間內(nèi)同時運(yùn)行。

  線程與進(jìn)程相似,是一段完成某個特定功能的代碼,是程序中單個順序的流控制。但與進(jìn)程不同的是,同類的多個線程共享一塊內(nèi)存空間和一組系統(tǒng)資源,所以系統(tǒng)在各個線程之間切換時,資源占用要比進(jìn)程小得多,正因如此,線程也被稱為輕量級進(jìn)程。一個進(jìn)程中可以包含多個線程。圖8-4所示是計(jì)時器程序進(jìn)程和線程之間的關(guān)系,主線程負(fù)責(zé)管理子線程,即子線程的啟動、掛起、停止等操作。

  

  ▲圖8-4 進(jìn)程和線程關(guān)系

  Java中的線程

  Java的線程類是java.lang.Thread類。當(dāng)生成一個Thread類的對象之后,一個新的線程就產(chǎn)生了。Java中每個線程都是通過某個特定Thread對象的方法run()來完成其操作的,方法run( )稱為線程體。

  下面是構(gòu)建線程類幾種常用的方法:

  public Thread()

  public Thread(Runnable target)

  public Thread(Runnable target, String name)

  public Thread(String name)

  參數(shù)target是一個實(shí)現(xiàn)Runnable接口的實(shí)例,它的作用是實(shí)現(xiàn)線程體的run()方法。目標(biāo)target可為null,表示由本身實(shí)例來執(zhí)行線程。name參數(shù)指定線程名字,但沒有指定的構(gòu)造方法,線程的名字是JVM分配的,例如JVM指定為thread-1、thread-2等名字。

  1、Java中的實(shí)現(xiàn)線程體方式1

  在Java中有兩種方法實(shí)現(xiàn)線程體:一是繼承線程類Thread,二是實(shí)現(xiàn)接口Runnable。下面我們先看看繼承線程類Thread方式。

  如果采用第1種方式,它繼承線程類Thread并重寫其中的方法 run(),在初始化這個類實(shí)例的時候,目標(biāo)target可為null,表示由本實(shí)例來執(zhí)行線程體。由于Java只支持單重繼承,用這種方法定義的類不能再繼承其他父類,例如代碼清單8-1,完整代碼請參考chapter8_1工程中chapter8_1代碼部分。

  【代碼清單8-1】

public class chapter8_1 extends Thread {

    boolean isRunning = true;

    int timer = 0;

    /**
     * 線程體代碼
     */
    @Override
    public void run() {
        while (isRunning) {
            try {
                Thread.currentThread().sleep(1000);
                timer++;
                System.out.println("逝去了 "+timer+" 秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {

        chapter8_1 t1 = new chapter8_1();

        t1.start();
        System.out.println("計(jì)時器啟動...");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try {
            String line = br.readLine();
            if (line.equalsIgnoreCase("1")) {
                t1.isRunning = false;
                /*t1.stop();*/
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    }

  在main主方法中通過new chapter8_1()創(chuàng)建子線程,并通過t1.start()方法啟動子線程,main主方法所在線程為主線程,主線程負(fù)責(zé)管理其他的子線程。本例進(jìn)程、主線程和子線程之間的關(guān)系如圖8-5所示。

  子線程啟動之后就開始調(diào)用run()方法,run()是一個線程體,我們在子線程中處理事情就是在這里編寫代碼實(shí)現(xiàn)的。本案例中子線程要做的事情就是:休眠1s,計(jì)時器加1,再反復(fù)執(zhí)行。Thread.currentThread().sleep(1000)就是休眠1s。

  為了能夠停止線程,我們在主線程中增加了一個標(biāo)識,通過在控制臺輸入一個字符

  “1”來改變該標(biāo)識t1.isRunning = false,從而結(jié)束這個線程。

  注意:

  事實(shí)上線程中有一個stop()方法也可以停止線程,但是由于這種方法會產(chǎn)生線程死鎖問題,所以在新版JDK中已經(jīng)廢止了,它的替代解決方法就是增加標(biāo)識,就是我們在本例中采用的方案。

  很多人覺得線程難理解,主要有兩個問題:

  線程休眠,既然線程已經(jīng)休眠了,程序的運(yùn)行速度還能提高嗎?

  線程體一般都進(jìn)行死循環(huán),既然線程死循環(huán),程序就應(yīng)該死掉了,就會沒有反應(yīng)。

  1.關(guān)于線程休眠問題

  對線程休眠問題頭痛的讀者,其實(shí)還是在用單線程的思維模式考慮問題,多數(shù)情況下我們的PC都是單CPU的,某個時間點(diǎn)只能有一個線程運(yùn)行。所謂多線程就是多個線程交替執(zhí)行就好像同時運(yùn)行似的。因此,休眠當(dāng)前線程可以交出CPU控制權(quán),讓其他的線程有機(jī)會運(yùn)行,多個線程之間只有交替運(yùn)行效率才是最高的,這就像我們開車過十字路口,只有我等等,讓你先過,你再等等讓他先過,才能保證最高效率,否則就會造成交通系統(tǒng)崩潰,對線程情況也是一樣的。因此,多線程中線程的休眠是程序運(yùn)行的最有效方式。

  2.關(guān)于線程體死循環(huán)問題

  在單線程中如果是死循環(huán),程序應(yīng)就會死掉,沒有反應(yīng),但是多線程中線程體(run方法)中的死循環(huán),可以保證線程一直運(yùn)行,如果不循環(huán)線程,則運(yùn)行一次就停止了。在上面的例子中線程體運(yùn)行死循環(huán),可以保證線程一直運(yùn)行,每次運(yùn)行都休眠1s,然后喚醒,再然后把時間信息輸出到控制臺。所以,線程體死循環(huán)是保證子線程一直運(yùn)行的前提。由于是子線程它不會堵塞主線程,就不會感覺到程序死掉了。但是需要注意的是有時我們確實(shí)執(zhí)行一次線程體,就不需要循環(huán)了。

  程序運(yùn)行后開始啟動線程,線程啟動后就計(jì)算逝去的時間,每過1s將結(jié)果輸出到控制臺。當(dāng)輸入1字符后線程停止,程序終止

  Java中的實(shí)現(xiàn)線程體方式2

  上面介紹繼承Thread方式實(shí)現(xiàn)線程體,下面介紹另一種方式,這種方式是提供一個實(shí)現(xiàn)接口Runnable的類作為一個線程的目標(biāo)對象,構(gòu)造線程時有兩個帶有Runnable target參數(shù)的構(gòu)造方法:

  Thread(Runnable target);

  Thread(Runnable target, String name)。

  其中的target就是線程目標(biāo)對象了,它是一個實(shí)現(xiàn)Runnable的類,在構(gòu)造Thread類時候把目標(biāo)對象(實(shí)現(xiàn)Runnable的類)傳遞給這個線程實(shí)例,由該目標(biāo)對象(實(shí)現(xiàn)Runnable的類)提供線程體run()方法。這時候?qū)崿F(xiàn)接口Runnable的類仍然可以繼承其他父類。

  請參看代碼清單8-2,這是一個Java AWT的窗體應(yīng)用程序,完整代碼請參考chapter8_2工程中chapter8_2_1代碼部分。

  【代碼清單8-2】

public class chapter8_2_1 extends Frame implements ActionListener, Runnable {

    private Label label;
    private Button button1;
    private Thread clockThread;
    private boolean isRunning = false;
    private int timer = 0;

    public chapter8_2_1() {
        button1 = new Button("結(jié)束計(jì)時");
        label = new Label("計(jì)時器啟動...");
        button1.addActionListener(this);
        setLayout(new BorderLayout());
        add(button1, "North");
        add(label, "Center");
        setSize(320480);
        setVisible(true);

        clockThread = new Thread(this);
        /* 線程體是Clock對象本身,線程名字為"Clock" */
        clockThread.start(); /* 啟動線程 */
        isRunning = true;
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        isRunning = false;
    }

    @Override

    public void run() {
        while (isRunning) {
            try {
                Thread.currentThread().sleep(1000);
                timer++;
                label.setText("逝去了 " + timer + " 秒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String args[]) {
        chapter8_2_1 a = new chapter8_2_1();
    }

}

  其中關(guān)于Java AWT知識本書就不在這里介紹了,有興趣的讀者可以自己看看相關(guān)書籍。在本例中構(gòu)建AWT窗體的應(yīng)用程序方式是繼承Frame類。采用第1種方式——繼承方式實(shí)現(xiàn)線程體是不可以的,因?yàn)镴ava是單繼承的,這個類不能既繼承Frame又繼承Thread。應(yīng)該采用第2種方式——實(shí)現(xiàn)Runnable接口方式。Runnable接口也有一個run()方法,它是實(shí)現(xiàn)線程體方法,其代碼處理與上一節(jié)是一樣。需要注意的是,在第2種方法中,創(chuàng)建了一個Thread成員變量clockThread,才用構(gòu)造方法new Thread(this)創(chuàng)建一個線程對象,其中創(chuàng)建線程使用的構(gòu)造方法是Thread(Runnable target),其中的this就是代表本實(shí)例,它是一個實(shí)現(xiàn)了Runnable接口的實(shí)現(xiàn)類。

  程序運(yùn)行結(jié)果如圖8-7所示,屏幕開始加載的時候線程啟動開始計(jì)算時間,1s更新一次UI,當(dāng)單擊“結(jié)束計(jì)時”按鈕時,停止計(jì)時。



  Java中的實(shí)現(xiàn)線程體方式3

  實(shí)現(xiàn)線程體方式3是實(shí)現(xiàn)線程體方式2的變種,本質(zhì)上還是實(shí)現(xiàn)線程體方式2,但是在Android應(yīng)用開發(fā)中經(jīng)常采用第3種方式。下面我們看第3種方式的計(jì)時器代碼清單8-3,完整代碼請參考chapter8_2工程中 chapter8_2_2代碼部分。

  【代碼清單8-3】

public class chapter8_2_2 extends Frame implements ActionListener {

    private Label label;
    private Button button1;
    private Thread clockThread;

    private boolean isRunning = false;
    private int timer = 0;

    public chapter8_2_2() {
        button1 = new Button("結(jié)束計(jì)時");
        label = new Label("計(jì)時器啟動...");
        button1.addActionListener(this);
        setLayout(new BorderLayout());
        add(button1, "North");
        add(label, "Center");
        setSize(320480);
        setVisible(true);

        /* 線程體是Clock對象本身,線程名字為"Clock" */
        clockThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (isRunning) {
                    try {
                        Thread.currentThread().sleep(1000);
                        timer++;
                        label.setText("逝去了 " + timer + " 秒");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        clockThread.start(); /* 啟動線程 */
        isRunning = true;
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        isRunning = false;
    }

    public static void main(String args[]) {
        chapter8_2_2 a = new chapter8_2_2();
    }

    }

  與第2種方式比較,我們發(fā)現(xiàn)Frame類不再實(shí)現(xiàn)Runnable接口了,而是在實(shí)例化Thread類的時候,定義了一個實(shí)現(xiàn)Runnable接口的匿名內(nèi)部類:

clockThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (isRunning) {
                    try {
                        Thread.currentThread().sleep(1000);
                        timer++;
                        label.setText("逝去了 " + timer + " 秒");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    });

  有關(guān)Java多線程的內(nèi)容還有很多,例如線程優(yōu)先級、線程同步等,由于這些內(nèi)容與本書關(guān)系不是很緊密,所以不再介紹了,有關(guān)其他的線程知識可以參考Java方面的書籍。接下來介紹一下Android中的線程。

  Android中的線程

  在Android平臺中多線程應(yīng)用很廣泛,在UI更新、游戲開發(fā)和耗時處理(網(wǎng)絡(luò)通信等)等方面都需要多線程。Android線程涉及的技術(shù)有:Handler;Message;MessageQueue;Looper;HandlerThread。

  Android線程應(yīng)用中的問題與分析

  為了介紹這些概念,我們把計(jì)時器的案例移植到Android系統(tǒng)上,按照在Frame方式修改之后的代碼清單8-4,完整代碼請參考chapter8_3工程中 chapter8_3代碼部分。

  【代碼清單8-4】

public class chapter8_3 extends Activity {

    private String TAG = "chapter8_3";
    private Button btnEnd;
    private TextView labelTimer;
    private Thread clockThread;
    private boolean isRunning = true;
    private int timer = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        btnEnd = (Button) findViewById(R.id.btnEnd);
        btnEnd.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                isRunning = false;
            }
        });

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

        /* 線程體是Clock對象本身,線程名字為"Clock" */
        clockThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (isRunning) {
                    try {
                        Thread.currentThread().sleep(1000);
                        timer++;
                        labelTimer.setText("逝去了 " + timer + " 秒");
                        Log.d(TAG, "lost  time " + timer);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        });

        clockThread.start(); /* 啟動線程 */

    }
    }

 

  系統(tǒng)拋出的異常信息是“Only the original thread that created a view hierarchy can touch its views”,在Android中更新UI處理必須由創(chuàng)建它的線程更新,而不能在其他線程中更新。上面的錯誤原因就在于此。

  現(xiàn)在分析一下上面的案例,在上面的程序中有兩個線程:一個主線程和一個子線程,它們的職責(zé)如圖8-10所示。

  由于labelTimer是一個UI控件,它是在主線程中創(chuàng)建的,但是它卻在子線程中被更新了,更新操作在clockThread線程的run()方法中實(shí)現(xiàn),代碼如下:

/* 線程體是Clock對象本身,線程名字為"Clock" */
clockThread = new Thread(new Runnable() {
    @Override
    public void run() {
        while (isRunning) {
            try {
                Thread.currentThread().sleep(1000);
                timer++;
                labelTimer.setText("逝去了 " + timer + " 秒");
                Log.d(TAG, "lost  time " + timer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
});

  這樣的處理違背了Android多線程編程規(guī)則,系統(tǒng)會拋出異?!癘nly the original thread that created a view hierarchy can touch its views”。

  要解決這個問題,就要明確主線程和子線程的職責(zé)。主線程的職責(zé)是創(chuàng)建、顯示和更新UI控件、處理UI事件、啟動子線程、停止子線程;子線程的職責(zé)是計(jì)算逝去的時間和向主線程發(fā)出更新UI消息,而不是直接更新UI。

  主線程的職責(zé)是顯示UI控件、處理UI事件、啟動子線程、停止子線程和更新UI,子線程的職責(zé)是計(jì)算逝去的時間和向主線程發(fā)出更新UI消息。但是新的問題又出現(xiàn)了:子線程和主線程如何發(fā)送消息、如何通信呢?

  在Android中,線程有兩個對象—消息(Message)和消息隊(duì)列(MessageQueue)可以實(shí)現(xiàn)線程間的通信。下面再看看修改之后的代碼清單8-5,完整代碼請參考chapter8_4工程中chapter8_4代碼部分。

  【代碼清單8-5】

public class chapter8_4 extends Activity {

    private String TAG = "chapter8_3";
    private Button btnEnd;
    private TextView labelTimer;
    private Thread clockThread;
    private boolean isRunning = true;
    private Handler handler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        btnEnd = (Button) findViewById(R.id.btnEnd);
        btnEnd.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                isRunning = false;
            }
        });

        handler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case 0:
                    labelTimer.setText("逝去了 " + msg.obj + " 秒");
                }
            }

        };

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

        /* 線程體是Clock對象本身,線程名字為"Clock" */
        clockThread = new Thread(new Runnable() {
            @Override

            public void run() {
                int timer = 0;
                while (isRunning) {
                    try {
                        Thread.currentThread().sleep(1000);
                        timer++;
                        /* labelTimer.setText("逝去了 " + timer + " 秒"); */
                        Message msg = new Message();
                        msg.obj = timer;
                        msg.what = 0;
                        handler.sendMessage(msg);
                        Log.d(TAG, "lost  time " + timer);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        clockThread.start(); /* 啟動線程 */

    }

  有的時候?yàn)榱藢ndroid代碼變得更加緊湊,把線程的創(chuàng)建和啟動編寫在一條語句中,如下面chapter8_5的代碼片段。代碼清單8-6所示,完整代碼請參考chapter8_5工程中 chapter8_5代碼部分。

  【代碼清單8-6】

new Thread() {
        @Override
        public void run() {
            int timer = 0;
            while (isRunning) {
                ry {
                    Thread.currentThread().sleep(1000);
                    timer++;
                    / labelTimer.setText("逝去了 " + timer + " 秒");
                    Message msg = new Message();
                    msg.obj = timer;
                    msg.what = 0;
                    handler.sendMessage(msg);
                    Log.d(TAG, "lost  time " + timer);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }.start();

  chapter8_5代碼看起來有些糊涂吧?chapter8_4和chapter8_5創(chuàng)建線程的區(qū)別是:chapter8_4采用Thread(Runnable target)構(gòu)造方法創(chuàng)建一個線程,需要提供一個Runnable接口對象,需要提供的參數(shù)是實(shí)現(xiàn)了Runnable接口的匿名內(nèi)部類對象。chapter8_5采用Thread()構(gòu)造方法創(chuàng)建一個線程,在這里采用了簡便的編程方法,直接新建一個Thread類,同時重寫run()方法。

  chapter8_5編程方法雖然晦澀難懂,而且違背了Java編程規(guī)范,程序結(jié)構(gòu)也比較混亂,但卻是Android習(xí)慣寫法,這主要源于Android對于減少字節(jié)碼的追求。究竟這兩種方式在性能上有多少差別呢?誠實(shí)地講我沒有做過測試和求證,在我看來就上面的程序而言它們之間不會有太大差別,由于本書要盡可能遵守Java編程規(guī)范和Android的編程習(xí)慣,因此本書中兩種編程方式都會采用,如果給大家?guī)聿槐憔凑堈徑狻?/p>

  運(yùn)行模擬器結(jié)果如圖8-1所示,加載屏幕后馬上開始計(jì)時,也可以單擊“停止計(jì)時”按鈕來停止計(jì)時。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Android學(xué)習(xí)筆記(三一):線程:Message和Runnable
Android的線程使用來更新UI
多線程實(shí)現(xiàn)方式
android幾種定時器機(jī)制及區(qū)別(轉(zhuǎn)載整理)
android——淺談歸納Handler各種用法
Android 的invalidate 與postInvalidate的用法
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服