android Thread和Runable區別 精講

android Thread和Runable區別,精講(有疑問)

http://www.cnblogs.com/yuhanghzsd/p/5581918.html



網上總是說Runable和Thread可以實現線程,這導致我對Thread和Runable有錯誤的理解,誰讓當時不求甚解,讓我一直以爲實現Runable可以開啓線程。

看過源碼後進行區分這兩者。

無論怎麼樣,線程都是通過Thread創建的。

其一:Runable只是一個接口,不會開啓一個線程,依舊是運行在UI線程中。                     

複製代碼
public interface Runnable {

    /**
     * Starts executing the active part of the class' code. This method is
     * called when a thread is started that has been created with a class which
     * implements {@code Runnable}.
     */
    public void run();
}
複製代碼

  可以看到,Runable在源碼中只有run方法,並且Runable可以在主線程執行修改UI的代碼,並且“OK”的執行一定是在輸出10個“runable”後,所以,Runable並沒有開啓線程,依舊是運行在UI線程中

複製代碼
protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        runnable.run();
        Log.d("hello", "OK");


    }
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for(int i = 0;i<10;i++) {
                    textView.setText("runable");
                    Log.d("hello", "runable");
                }
            }
};
複製代碼

 

其二:Thread如何開啓的線程,和Runable什麼關係?

源碼部分:

public class Thread implements Runnable {

 

最直接的一部分,實現Runable的接口。下面看看Thread的構造函數。

public Thread() {
    create(null, null, null, 0);
}

 這是其中之一的構造函數,調用了create()方法,其他構造函數依然調用該方法。下面看看create()方法。

複製代碼
private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
        Thread currentThread = Thread.currentThread();
        if (group == null) {
            group = currentThread.getThreadGroup();
        }

        if (group.isDestroyed()) {
            throw new IllegalThreadStateException("Group already destroyed");
        }

        this.group = group;

        synchronized (Thread.class) {
            id = ++Thread.count;
        }

        if (threadName == null) {
            this.name = "Thread-" + id;
        } else {
            this.name = threadName;
        }
    
        this.target = runnable;

     this.stackSize = stackSize; this.priority = currentThread.getPriority(); this.contextClassLoader = currentThread.contextClassLoader; // Transfer over InheritableThreadLocals. if (currentThread.inheritableValues != null) { inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues); } // add ourselves to our ThreadGroup of choice this.group.addThread(this); }
複製代碼

 

 雖然比較多,但是只需要看這一行代碼,this.taeget = runable;而在Thread類的run()方法和start()方法中,

 

  public void run() {
        if (target != null) {
            target.run();
        }
    }

  

複製代碼
/**
     * Starts the new Thread of execution. The <code>run()</code> method of
     * the receiver will be called by the receiver Thread itself (and not the
     * Thread calling <code>start()</code>).
     *
     * @throws IllegalThreadStateException - if this thread has already started.
     * @see Thread#run
     */
    public synchronized void start() {
        checkNotStarted();

        hasBeenStarted = true;

        nativeCreate(this, stackSize, daemon);
    }
複製代碼

 

 

 

這兩個形成了兩個方法的區別:

 

  •   start: 

           用start方法來啓動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。通過調用Thread類的start()方法來啓動一個線程,這時此線程處於就緒(可運行)狀態,並沒有運行,一旦得到cpu時間片,就開始執行run()方法,這裏方法 run()稱爲線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程隨即終止。

  •   run:
      run()方法只是類的一個普通方法而已,如果直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑還是隻有一條,還是要順序執行,還是要等待run方法體執行完畢後纔可繼續執行下面的代碼,這樣就沒有達到寫線程的目的。總結:調用start方法方可啓動線程,而run方法只是thread的一個普通方法調用,還是在主線程裏執行。這兩個方法應該都比較熟悉,把需要並行處理的代碼放在run()方法中,start()方法啓動線程將自動調用 run()方法,這是由jvm的內存機制規定的。並且run()方法必須是public訪問權限,返回值類型爲void.。

 

這樣也就很清晰可見,那麼如何開啓線程,

    其中之一,和上文類比。

複製代碼
 Thread t = new Thread(runnable);
        t.start();
        Log.d("hello","ok");
    }
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
           textview.settext(“runable”);
for(int i = 0;i<10;i++) { Log.d("hello", "runable"); } } };
複製代碼
  1. 執行Thread的構造方法,然後執行oncreate方法。this.target = runable; 
  2. 執行t.start();等得到cpu時間片,執行run()方法。
  3. 執行run()方法,target.run();  (也就是在子線程執行runable.run()。)

根據執行結果也看到真正的開啓了線程,“OK”的輸出並不按代碼執行順序。

 

複製代碼
06-13 19:32:26.252 21369-21369/? D/hello: ok
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
06-13 19:32:26.252 21369-21429/? D/hello: runable
複製代碼

 但是可以修改Ui(textview.settext)?!!!!!!執行的是runable.run()。(不應該在子線程執行這句話嗎?子線程不是不能修改UI嗎?)

   

爲其添加按鈕監聽,打印線程 t 的名字,爲Thread-76777  

  button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                t.start();
            }
        });

 得到我想要的結果:: FATAL EXCEPTION: Thread-76777 ,android.view.ViewRootImpl$CalledFromWrongThreadException:

    那麼,問題在於。爲什麼上述情況可以修改,而添加按鈕點擊後執行t.start()無法修改UI

  希望有人看到幫我解答一下這個疑問,如果本文有錯誤也希望指出。轉載請註明出處http://www.cnblogs.com/yuhanghzsd/p/5581918.html 

原因:

onCreate中極短的時間內, viewRootImpl還沒有創建完成, new Thread()是可以“修改”ui的.

因爲那個時候viewRootImpl還沒有創建.

當使用按鈕之後, 就不行了. 因爲這個時候的view樹肯定是已經創建完畢了. 
只有創建該View的線程才能修改和更新改view.
因爲ViewRootImpl是在ActivityThread類中的主線程創建的. 所以只有主線程能更新ui.

 

 END!

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