更快的執(zhí)行
在多核處理器上,將一個程序的分為多個片段,在每個單獨的處理上運行每個片段。利用上更多的處理器顯然會使程序運行的更加快。
但并發(fā)通常是提高運行在單核處理器上的程序性能。理論上,單處理器上運行并發(fā)任務(wù)增加了上下文切換的時間,這會看起來比順序執(zhí)行開銷更大。
但實際上,阻塞的出現(xiàn)讓情況變得不同。如果程序中某個任務(wù)出現(xiàn)線程阻塞(通常是I/O),這時候使用并發(fā)編寫程序,可以保證其他任務(wù)可以繼續(xù)執(zhí)行。
基本線程機制
代表所執(zhí)行的對象的 Runnable
實現(xiàn) Runnable
接口 唯一 public abstract void run()
方法,代表所須執(zhí)行的命令。
public class Demo implements Runnable {
private int countDown = 10;
@Override
public void run() {
while (countDown > 0){
System.out.println(countDown--);
Thread.yield(); //讓步
}
}
}
帶返回值的 Callable
實現(xiàn) Callable
唯一方法 V call()
方法。
public class CallDemo implements Callable<String> {
@Override
public String call() throws Exception {
return "CallDemo" + super.toString();
}
}
Future<?> submit(Runnable task)
方法返回了 Future<?>
接口的實例。
我們可以使用 isDone 來檢驗 Future
對象是否執(zhí)行完異步的任務(wù),也可以不檢查直接使用 get()
獲取異步任務(wù)的返回對象,這個過程也會阻塞到結(jié)果異步任務(wù)執(zhí)行完。
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> future = executorService.submit(new CallDemo());
executorService.shutdown();
System.out.println(future.get());
}
}
FutureTask
FutureTask<V>
實現(xiàn)了 RunnableFuture<V>
接口。而 RunnableFuture<V>
多重繼承了 Runnable
和 Future<V>
方法。
public class FutureTask<V> implements RunnableFuture<V>{...}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
所以 FutureTask
可以直接在構(gòu)造器中聲明所執(zhí)行的異步任務(wù),即 Runnable
對象。在提交到執(zhí)行器(ExecutorService
)之后,可以通過自己的 get()
方法獲取異步任務(wù)的返回值。
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
FutureTask<String> futureTask = new FutureTask<>(new CallDemo());
executorService.submit(futureTask);
System.out.println(futureTask.get());//忽略了 try catch
executorService.shutdown();
}
}
Thread
Thread.yield()
的調(diào)用是對線程調(diào)度器的一個建議,表示自己已經(jīng)執(zhí)行完了重要的代碼,可以切換到其他任務(wù)。
public static void main(String[] args) {
Thread thread = new Thread(new Demo());
thread.start();
}
Thread.start()
方法為該線程執(zhí)行必須的初始化操作。然后在新的線程中調(diào)用 Runnable.run()
方法。
Executor
Executor
允許你管理異步任務(wù)的執(zhí)行,而無需顯示的管理線程的生命周期。類命令模式的設(shè)計使其只能提供了一個 excute()
方法。
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executorService.execute(new Demo());
}
executorService.shutdown();
System.out.println("main end");
}
}
3 3 2 3 3 3 2 1 2 2 2 1 main end 1 1 1
Process finished with exit code 0
shutDown()
方法保證在這之后有新的任務(wù)提交給 Executor
,而后在 Executor
執(zhí)行完成任務(wù)后盡快退出。
- CachedThreadPool
CachedThreadPool
會為創(chuàng)建所屬數(shù)量的線程,只有在回收線程時停止創(chuàng)建新的線程,而 maximumPoolSize
值為 Integer.MAX_VALUE
,有可能因線程數(shù)過多而倒是 OOM。
- FixedThreadPool
FixedThreadPool 可以限制線程數(shù)量。一次性的預(yù)先執(zhí)行代價高昂的線程分配,而不用為每個任務(wù)都付出創(chuàng)建線程的開銷。
- SingleThreadExecutor
SingleThreadExecutor
是線程數(shù)為 1 的 FixedThreadPool
的串行隊列。maximumPoolSize
值為 1
, 會因為任務(wù)堆積而導(dǎo)致 OOM。
Sleep
sleep()
使任務(wù)中止運行一段時間。
public class Demo implements Runnable {
private int countDown = 3;
@Override
public void run() {
try {
while (countDown > 0) {
System.out.println(countDown--);
TimeUnit.MILLISECONDS.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
權(quán)重(Priority)
Thread.currentThread().setPriority(priority);
可以為任務(wù)設(shè)置優(yōu)先級。確保線程調(diào)度機制能優(yōu)先運行最高優(yōu)先級的任務(wù)。
讓步(yield)
在完成一次迭代后,可以給線程調(diào)度機制一個建議,調(diào)用 yield()
方法讓步,表示這個時刻可以運行 具有相同優(yōu)先級的 的任務(wù)。
后臺線程(Daemon)
后臺(daemon)線程指程序在運行時在后臺提供一種通用服務(wù)的線程,并且這種線程不屬于程序中不可或缺的部分。當(dāng)所有非后臺線程結(jié)束后,程序也中止了。
Thread daemon = new Thread(new Demo());
daemon.setDaemon(true);
daemon.start();
Process finished with exit code 0 (不執(zhí)行 runnable 中的任務(wù))
線程工廠(ThreadFactory)
我們可以繼承 ThreadFactory
并實現(xiàn)其 newThread()
方法,來配置 ExecutorService 所創(chuàng)建線程的屬性。
public class DaemonThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread();
thread.setDaemon(true);
return thread;
}
}
使用 ThreadFactory
來構(gòu)建 newCachedThreadPool
public class Main {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool(new DaemonThreadFactory());
service.execute(new Demo());
service.shutdown();
}
}