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佇列都滿了的情況下,對新任務採取的處理策略。