更快的執行
在多核處理器上,將一個程序的分為多個片段,在每個單獨的處理上運行每個片段。利用上更多的處理器顯然會使程序運行的更加快。
但并發通常是提高運行在單核處理器上的程序性能。理論上,單處理器上運行并發任務增加了上下文切換的時間,這會看起來比順序執行開銷更大。
但實際上,阻塞的出現讓情況變得不同。如果程序中某個任務出現線程阻塞(通常是I/O),這時候使用并發編寫程序,可以保證其他任務可以繼續執行。
基本線程機制
代表所執行的對象的 Runnable
實現 Runnable
接口 唯一 public abstract void run()
方法,代表所須執行的命令。
public class Demo implements Runnable {
private int countDown = 10;
@Override
public void run() {
while (countDown > 0){
System.out.println(countDown--);
Thread.yield(); //讓步
}
}
}
帶返回值的 Callable
實現 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
對象是否執行完異步的任務,也可以不檢查直接使用 get()
獲取異步任務的返回對象,這個過程也會阻塞到結果異步任務執行完。
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>
實現了 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
可以直接在構造器中聲明所執行的異步任務,即 Runnable
對象。在提交到執行器(ExecutorService
)之后,可以通過自己的 get()
方法獲取異步任務的返回值。
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()
的調用是對線程調度器的一個建議,表示自己已經執行完了重要的代碼,可以切換到其他任務。
public static void main(String[] args) {
Thread thread = new Thread(new Demo());
thread.start();
}
Thread.start()
方法為該線程執行必須的初始化操作。然后在新的線程中調用 Runnable.run()
方法。
Executor
Executor
允許你管理異步任務的執行,而無需顯示的管理線程的生命周期。類命令模式的設計使其只能提供了一個 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()
方法保證在這之后有新的任務提交給 Executor
,而后在 Executor
執行完成任務后盡快退出。
- CachedThreadPool
CachedThreadPool
會為創建所屬數量的線程,只有在回收線程時停止創建新的線程,而 maximumPoolSize
值為 Integer.MAX_VALUE
,有可能因線程數過多而倒是 OOM。
- FixedThreadPool
FixedThreadPool 可以限制線程數量。一次性的預先執行代價高昂的線程分配,而不用為每個任務都付出創建線程的開銷。
- SingleThreadExecutor
SingleThreadExecutor
是線程數為 1 的 FixedThreadPool
的串行隊列。maximumPoolSize
值為 1
, 會因為任務堆積而導致 OOM。
Sleep
sleep()
使任務中止運行一段時間。
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();
}
}
}
權重(Priority)
Thread.currentThread().setPriority(priority);
可以為任務設置優先級。確保線程調度機制能優先運行最高優先級的任務。
讓步(yield)
在完成一次迭代后,可以給線程調度機制一個建議,調用 yield()
方法讓步,表示這個時刻可以運行 具有相同優先級的 的任務。
后臺線程(Daemon)
后臺(daemon)線程指程序在運行時在后臺提供一種通用服務的線程,并且這種線程不屬于程序中不可或缺的部分。當所有非后臺線程結束后,程序也中止了。
Thread daemon = new Thread(new Demo());
daemon.setDaemon(true);
daemon.start();
Process finished with exit code 0 (不執行 runnable 中的任務)
線程工廠(ThreadFactory)
我們可以繼承 ThreadFactory
并實現其 newThread()
方法,來配置 ExecutorService 所創建線程的屬性。
public class DaemonThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread();
thread.setDaemon(true);
return thread;
}
}
使用 ThreadFactory
來構建 newCachedThreadPool
public class Main {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool(new DaemonThreadFactory());
service.execute(new Demo());
service.shutdown();
}
}