Android多執行緒相關(一)
Thread的run()和start()
首先看下面的程式碼演示
public static void main(String[] args) { Thread newThread = new Thread(){ @Override public void run() { super.run(); System.out.println(Thread.currentThread().getName()); } }; newThread.run(); newThread.start(); } 列印結果為: main Thread-0
如程式碼所示,如果直接呼叫run()方法,程式碼是在Thread物件所在的當前執行緒執行的。而呼叫start()方法,run()方法裡的程式碼,才是在新執行緒裡執行的。關於這個區別,可以看看Thread類中關於這樣個方法的註解
* Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread. * <p> * The result is that two threads are running concurrently: the * current thread (which returns from the call to the * <code>start</code> method) and the other thread (which executes its * <code>run</code> method). * <p> * It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. public synchronized void start() {....}
* If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. @Override public void run() { if (target != null) { target.run(); } }
Thread和Runnable
在java中,一般實現建立執行緒執行任務的方法有兩種,一種是直接繼承Thread類,另一種是實現Runnable介面。區別在於:
- java是單繼承,但可以實現多個介面。所以從類的擴充套件來看,實現Runnable介面會比繼承Thread類,更靈活。
-
Runnable只是一個介面,它本身並不具備建立執行緒執行任務的功能。它只是定義了一個可執行的任務。實際上,任務的執行需要Thread呼叫Runnable定義的run方法。
上面THread類中預設實現的run()方法,顯示了兩者的關係,其中的target就是Runnable物件
/* What will be run. */ private Runnable target;
Runnable和Callable
Callable和Runnable都是用來定義可非同步執行的任務的介面,Runnable是JDK1.0就有的API,Callable是JDK1.5增加的API。兩者的區別在於:
- Callable的call()方法有返回值並且可以丟擲異常,而Runnable的run()方法不具備這些特徵。
- 執行Callable任務,可以拿到一個Future物件,表示非同步執行的結果。通過Future物件可以瞭解任務執行情況,可取消任務的執行,也可以獲得執行的結果。
- Thread類只支援執行Runnable介面,不支援執行Callable介面
FutureTask
FutureTask類實現了RunnableFuture<T>介面,而RunnbleFuture<T>介面繼承了Runnable介面和Future<T>介面,也就是說FutureTask<T>類是同時實現了Runnable介面和Future<T>介面。所以它既能當做Runnable被執行緒執行,又能作為Future<T>得到Callable<T>的返回值。FutureTask還可以讓呼叫者知道任務什麼時候執行完,並獲得執行緒執行完成後返回的結果。
public class FutureTask<V> implements RunnableFuture<V>{ public FutureTask(Callable<V> callable) { } public FutureTask(Runnable runnable, V result) { } } public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
下面是一個結合Callable、Future和FutureTask的常見用法
executorService = new ScheduledThreadPoolExecutor(5); Callable<String> task = new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(2000); System.out.println(Thread.currentThread().getName()); System.out.println("callable is running"); return "callable is done"; } }; /** * 用法一 */ //Future future = executorService.submit(task); //executorService.shutdown(); //future.get(); /** * 用法二 */ FutureTask futureTask = new FutureTask(task); executorService.submit(futureTask); executorService.shutdown(); /** * 用法三 */ //new Thread(futureTask).start(); System.out.println(Thread.currentThread().getName()); System.out.println("main is running"); try { System.out.println(futureTask.get()); System.out.println("main get result"); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }
synchronized的理解
我們通常用到synchronized關鍵字是這樣
public void testSynchronized(){ synchronized(this){ System.out.println("test is start"); try { Thread.sleep(2000); System.out.println("test is over"); } catch (InterruptedException e) { e.printStackTrace(); } } }
或者是這樣
public synchronized void testSynchronized(){ System.out.println("test is start"); try { Thread.sleep(2000); System.out.println("test is over"); } catch (InterruptedException e) { e.printStackTrace(); } }
也就是說,synchronized既可以加在一段程式碼上,也可以加在方法上。看上去像是第一種鎖的是一個物件,而第二種鎖的是方法裡的程式碼。但實質上並不是這樣。
synchronized鎖住的是括號裡的物件,而不是程式碼。對於非static的synchronized方法,鎖的就是物件本身也就是this。synchronized(this)以及非static的synchronized方法,只能防止多個執行緒執行同一個物件的同步程式碼段。
當synchronized鎖住一個物件後,別的執行緒如果也想拿到這個物件的鎖,就必須等待這個執行緒執行完成釋放鎖,才能再次給物件加鎖,這樣才達到執行緒同步的目的。即使兩個不同的程式碼段,都要鎖同一個物件,那麼這兩個程式碼段也不能在多執行緒環境下同時執行。
再看下下面的程式碼
public void testSynchronized(){ synchronized(Account.class){ System.out.println("test is start"); try { Thread.sleep(2000); System.out.println("test is over"); } catch (InterruptedException e) { e.printStackTrace(); } } } ...... ...... for (int i = 0;i < 3;i++){ Account account = new Account(); Thread thread = new Thread(){ @Override public void run() { super.run(); account.testSynchronized(); } }; thread.start(); }
程式碼中,每個子執行緒中,執行的都是不同的Account物件的testSynchronized方法。那麼要讓testSynchronized方法不被多執行緒同時執行,有兩種方式可以實現:
- 如程式碼所示,用synchronized(Account.class),實現了全域性鎖的效果,鎖住了程式碼段。
- 也可以定義為靜態方法,靜態方法可以直接用類名加方法名呼叫,方法內無法使用this。所以鎖的不是this物件,而是類的Class物件。static synchronized修飾的方法也相當於全域性鎖,鎖住了方法內的程式碼。