1. 程式人生 > >Android 中多執行緒的簡單使用

Android 中多執行緒的簡單使用

一、多執行緒的實現
1.最簡單的啟動一下新執行緒

private void startNewThread(){
        new Thread(){
            @Override
            public void run() {
                //耗時操作
            }
        }.start();
    }

或者:

 private void startNewThread(){
       new Thread(new Runnable() {
           @Override
           public
void run() { //耗時操作 } }){}.start(); }

實際上檢視原始碼可以知道:Thread也是一個Runable,它實現Runable介面,在Thread類中有一個Runable型別的target欄位,代表這個要執行在這個子執行緒中的任務。
Thread中預設的run方法原始碼為:

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

2.執行緒中waite、sleep、join、yield方法
wait(): 當一個執行緒執行到wait() 方法時,它就進入到了一個和該物件相關的等待池中,同時失去(釋放)了物件的機鎖,使得其他執行緒可以訪問。使用者可以使用notify,notifyAll或者指定睡眠時間來喚醒當前等待池中的執行緒。
sleep(): 該方法時Thread的靜態函式,作用是使呼叫執行緒進入睡眠狀態。因為sleep()是Thread()類的static 方法。因此他不能改變物件的機鎖。所以當一個synchronized快中呼叫sleep()方法時,執行緒雖然睡眠了,但是物件的機鎖並沒有被釋放,其他執行緒無法訪問到這個物件。(即使睡著也持有這物件鎖)
join(): 等待目標執行緒執行完成之後,再繼續執行。
yield(): 執行緒禮讓,目標執行緒由執行狀態轉換為就緒狀態,也就是讓出執行許可權,讓其他執行緒得以優先執行,但其他執行緒是否優先執行時未知的。
簡單示例:

wait()示例:

public class MyClass {
//    用於等待、喚醒的物件
    private static Object sLockObject = new Object();

    static void waitAndNotityAll() {
        System.out.println("主執行緒中執行");
        //        建立並啟動子執行緒
        Thread thread = new WaiteThread();
        thread.start();
        long startTime = System.currentTimeMillis();
        try {
            synchronized (sLockObject) {
                System.out.println("主執行緒等待");
                sLockObject.wait();
            }
        } catch (Exception e) {

        }

        long timeMs = System.currentTimeMillis() - startTime;
        System.out.println("主執行緒繼續--等待耗時: " + timeMs + " ms");
    }

    //    等待執行緒
    static class WaiteThread extends Thread {
        @Override
        public void run() {
            try {
                synchronized (sLockObject) {
                    Thread.sleep(3000);
                    sLockObject.notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args){
        waitAndNotityAll();
    }
}
執行結果:
    主執行緒中執行
    主執行緒等待
    主執行緒繼續--等待耗時: 3001 ms

join() 示例:

public class JoinTest {
    static void joinDemo(){
        Worker worker1 = new Worker("worker-1");
        Worker worker2 = new Worker("worker-2");
        worker1.start();
        System.out.println("啟動執行緒1");

        try {
//            呼叫worker1的join函式,主執行緒會阻塞直到worker1執行完成
            worker1.join();
            System.out.println("啟動執行緒2");
            worker2.start();
            worker2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主執行緒繼續執行");
    }

    static class Worker extends Thread{

        public Worker(String name){
            super(name);
        }

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("work in " +getName());
        }
    }

    public static void main(String[] args){
        joinDemo();
    }
}

執行結果:
    啟動執行緒1
    work in worker-1
    啟動執行緒2
    work in worker-2
    主執行緒繼續執行

yield()示例:

public class YieldTest {

    public static final int MAX = 5;

    static class YieldThread extends Thread {
        public YieldThread(String name) {
            super(name);
        }

        public synchronized void run() {
            for (int i = 0; i < MAX; i++) {
                System.out.printf("%s priority: [%d]------> %d\n", this.getName(), this.getPriority(), i);
//                當i==2 是,呼叫當前執行緒的yield函式。
                if (i == 2) {
                    Thread.yield();
                }
            }
        }
    }

    static void yieldDemo() {
        YieldThread t1 = new YieldThread("thread-1");
        YieldThread t2 = new YieldThread("thread-2");
        t1.start();
        t2.start();
    }

    public static void main(String[] args) {
        yieldDemo();
    }

}
可能沒有效果

3、與多執行緒相關的方法—–Callable、Future、FutureTask
Callable 與Runable的功能大致相似,不同的是Callable是一個泛型介面,它有一個泛型的引數V ,該介面有一個返回值(型別為V)的call 函式而Runable 的run()函式不能將結果返回給客戶程式。Callable的宣告如下:

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

而 Future為執行緒池制定了一個可管理的任務標準,它提供了對Runable或者Callable任務的執行結果進行取消,查詢是否完成、獲取結果、設定結果操作、分別對應cancel、isDone、get、set函式。get方法會阻塞,直到任務返回結果。future宣告如下:

public interface Future<V> {
//取消任務
    boolean cancel(boolean mayInterruptIfRunning);

 //任務是否已經取消
    boolean isCancelled();

   //任務是否已經完成
    boolean isDone();

  //獲取結果,如果任務未完成,則等待,直到完成,因此該函式會阻塞
    V get() throws InterruptedException, ExecutionException;

//獲取結果,如果還未完成那麼等待,直達timeout 或者返回結果,該函式會阻塞
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Future 只是定義了一些規範的介面,而FutureTask則是它的實現類。FutureTask實現了RunableFuture< V > 而RunableFuture又 繼承自 Runable,和Ruture< V > 這兩個介面,因此FutureTask具備了他們的能力。FutureTask程式碼如下:

public class FutureTask<V> implements RunnableFuture<V> {
.......
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

FutureTask會像Thread包裝Runable 那樣包裝 Callable,或者Runable
參考FutureTask的構造方法:

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
 public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

其中第二個構造方法,呼叫Executors.callable()函式將一個runnable轉換成一個實現Callable介面的類。參考程式碼:

   public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }
       /**
     * A callable that runs given task and returns given result
     */
    static final class RunnableAdapter<T> implements Callable<T> {
        final Runnable task;
        final T result;
        RunnableAdapter(Runnable task, T result) {
            this.task = task;
            this.result = result;
        }
        public T call() {
            task.run();
            return result;
        }
    }

由於FutureTask實現了Runable ,因此,它既可以通過Thread包裝來直接執行,也可以提交給ExcuteService來執行。並且還可以直接通過get()函式獲取執行結果,該函式會阻塞,知道結果返回。因此FutureTask既是Future,Runable,又包裝了Callable,(如果Runnable最終也會被轉換為Callable),他是這兩者的合體。
程式碼示例:

public class FutureDemo {
//    執行緒池
    static ExecutorService mExecutor  = Executors.newSingleThreadExecutor();

    public static void main(String[] args){
        try {
            futureWithRunable();
            futureWithCallable();
            futureTask();
        }catch (Exception e){

        }
    }

    private static void futureTask() throws ExecutionException, InterruptedException {
        FutureTask<Integer> futureTask  =new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return fibc(20);
            }
        }) ;
        //提交futureTask
        mExecutor.submit(futureTask);
        System.out.println("future result from futureTask : "+futureTask.get());
    }

    private static void futureWithCallable() throws ExecutionException, InterruptedException {
       final Future<Integer> result2 = mExecutor.submit(new Callable<Integer>() {
           @Override
           public Integer call() throws Exception {
               return fibc(20);
           }
       }) ;
        System.out.println("future reuslt from callable: "+result2.get()) ;
    }

    private static void futureWithRunable() throws ExecutionException, InterruptedException {
//        提交runable,沒有返回值,future沒有資料
        Future<?> result = mExecutor.submit(new Runnable() {
            @Override
            public void run() {
                fibc(20);
            }
        });

        System.out.println("future result from runable : "+result.get());
    }

    private static int fibc(int num){
        if(num==0){
            return 0;
        }
        if(num==1){
            return 1 ;
        }
        return fibc(num-1) +fibc(num-2);
    }
}

二、執行緒池
執行緒池的優點:
(1) 重用存在的執行緒,減少物件的建立,銷燬的開銷
(2)可有效的控制最大併發執行緒數,提高系統資源的使用率,同時避免過多的資環競爭,避免堵塞;
(3) 提供定時執行,定期執行,單執行緒、併發數控制等功能。

執行緒池都實現了ExecuteService介面,該介面定義了執行緒池需要實現的介面。如submit、excute、shutdow、等方法
它的實現有 ThreadPoolExecutor和ScheduledThreadPoolExecutor。

1、啟動指定數量的執行緒——ThreadPoolExecutor
ThreadPoolExecutor的構造方法如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

corePoolSize: 執行緒池所儲存的核心執行緒數,執行緒池啟動後預設是空的,只有任務來臨時才會建立執行緒以處理請求。prestartCoreThreads方法可以線上程池啟動後即啟動所有核心執行緒一等待任務。

maximumPoolSize: 執行緒池允許建立的最大執行緒數,當workQueue使用無界佇列時(如LinkedBlockingQueue),則此引數無效。它與corePoolSize的作用是調整“執行緒池中實際執行的執行緒數量” 。例如,當新任務提交給執行緒池時,如果執行緒池中的執行執行緒數量小於corePoolSize,則建立新執行緒來處理請求,如果此時,執行緒池中的執行執行緒數量大於corePoolSize,但小於maximumPoolSize,則僅當阻塞佇列滿時才建立新執行緒,如果設定的corePoolSize 和maximumPoolSize 相同,則建立了固定大小的執行緒池,如果將maxiumPoolSize設定為基本的無界值(如: Integer.MAX_VALUE),則允許執行緒池適應任意數量的併發任務。

keepAliveTime: 當前執行緒池執行緒總數大於核心執行緒數時,終止多餘的空閒執行緒的時間。

Unit: keepAliveTime引數的時間單位,可選值有毫秒,秒,分等。

workerQueue: 任務佇列,如果當前執行緒池,達到核心執行緒數corePoolSize,且當前所有執行緒都處於活動狀態時,則將新加入的任務放到此佇列中。

threadFactory: 執行緒工廠,讓使用者可以定製執行緒的建立過程,通常不需要設定。

Handle : 決絕策略,當執行緒池與workQueue佇列都滿了的情況下,對新任務採取的處理策略。