線程可以驅動任務,我們通過實現 Runnable 接口來提供,需要實現 Runnable 接口的 run() 方法。
package concurrency;
/**
* Created by wwh on 16-3-24.
*/
public class LiftOff implements Runnable {
protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public LiftOff() {}
public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status() {
return "#" + id + "(" +
(countDown > 0 ? countDown : "LiftOff!") + "), ";
}
public void run() {
while (countDown-- > 0) {
System.out.println(status());
/* Thread.yield() 是對線程調度器的一種建議,可以將CPU從一個線程轉移給另一個線程 */
Thread.yield();
}
}
}
package concurrency;
/**
* Created by wwh on 16-3-24.
*/
public class MainThread {
public static void main(String[] args) {
LiftOff lauch = new LiftOff();
lauch.run();
}
}
我們也可以通過繼承 Thread 類覆蓋 run() 方法來實現線程類,但繼承Thread類有一個缺點就是單繼承,而實現Runnable接口則彌補了它的缺點,可以實現多繼承。而且實現 Runnable 接口適合多線程共享資源,繼承 Thread 類適合各個線程完成自己的任務,因為繼承 Thread 類相當於每個線程有一份各自的資源,而實現 Runnable 還可以讓多個線程共享一份代碼。
將 Runnable 對象轉變為工作任務的傳統方式是將它提交給一個 Thread 構造器。
public class BasicThreads {
public static void main(String[] args) {
for (int i = 0; i < 5; ++i) {
new Thread(new LiftOff()).start();
}
System.out.println("Waiting for LiftOff");
}
}
Thread 構造器只需要一個 Runnable 對象。調用 Thread 對象的 start() 方法為該線程執行必須的初始化操作,然後內部調用 Runnable 的 run() 方法。
Java SE5 並發包中引入執行器可以為我們管理線程 Thread 對象。
package concurrency;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by wwh on 16-3-24.
*/
public class CachedThreadPool {
public static void main(String []args) {
/* ExecutorServive 是具有聲明周期的 Executor */
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 5; ++i) {
exec.execute(new LiftOff());
}
/* shutdown 被用來防止新任務被提交給 Executor */
exec.shutdown();
}
}
ThreadPool 種類很多。如下圖:
包括 如果我們希望任務完成時能夠返回一個值,那麼可以實現 Callable 接口而不是 Runnable 接口,實現 Callable 接口要求覆蓋 Call() 方法。 線程的優先級將該線程的重要性傳遞給了調度器。調度器會傾向於讓優先級高的線程先運行。 一個線程可以在其他線程之上調用 join() 方法,其效果是等待一段時間直到第二個線程結束才繼續執行。join() 調用時可以攜帶超時參數。 多個線程可能出現訪問共享資源的情況。 以上代碼沒有進行同步,多個線程同時增加計數器。所以導致結果不正確。 Java 為我們提供了幾種方式。 synchronzied:包括兩種用法,synchronzied 方法和 synchronized 塊。對於有 synchronzied 關鍵字修飾的類方法或代碼塊,執行時首先要獲取該類實例的鎖,執行完畢後釋放。在執行過程中要有其它線程等請求該 synchronzied 方法或代碼塊則被阻塞。 這幾種方式後續會詳細解釋。
CacheThreadPool:為每個任務都創建一個線程池
FixedThreadPool:一次性預先分配好固定大小的線程
SingleThreadExecutor:線程數唯一,提交多個任務會排隊等候
ScheduledThreadPool:創建一個定長線程池,支持定時及周期性任務執行。<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjwvcD4NCjxociAvPg0KPGgyIGlkPQ=="從任務中產生返回值">從任務中產生返回值
package concurrency;
import java.util.ArrayList;
import java.util.concurrent.*;
/**
* Created by wwh on 16-3-24.
*/
class TaskWithResult implements Callable
優先級
package concurrency;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by wwh on 16-3-24.
*/
public class SimplePriorities implements Runnable {
private int countDown = 5;
private volatile double d;
private int priority;
public SimplePriorities(int priority) {
this.priority = priority;
}
public String toString() {
return Thread.currentThread() + ": " + countDown;
}
public void run() {
Thread.currentThread().setPriority(priority);
while (true) {
/* 這裡只有循環次數比較大才能看出優先級的優勢 */
for (int i = 0; i < 100000000; ++i) {
d += (Math.PI + Math.E) / (double)i;
if (i % 1000 == 0) {
Thread.yield();
}
}
System.out.println(this);
if (--countDown == 0) {
return;
}
}
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 5; ++i) {
exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));
}
exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));
exec.shutdown();
}
}
加入一個線程
package concurrency;
/**
* Created by wwh on 16-3-24.
*/
class Sleeper extends Thread {
private int duration;
public Sleeper(String name, int sleepTime) {
super(name);
duration = sleepTime;
start();
}
public void run() {
try {
sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(getName() + " was interrupted. " +
"isInterrupted(): " + isInterrupted());
return;
}
System.out.println(getName() + " has awakened");
}
}
class Joiner extends Thread {
private Sleeper sleeper;
public Joiner(String name, Sleeper sleeper) {
super(name);
this.sleeper = sleeper;
start();
}
public void run() {
try {
/* 等待線程 */
sleeper.join();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("Interrupted");
}
System.out.println(getName() + " join completed");
}
}
public class Joining {
public static void main(String[] args) {
Sleeper
sleepy = new Sleeper("Sleepy", 1500),
grumpy = new Sleeper("Grumpy", 1500);
Joiner
drpey = new Joiner("Dopey", sleepy),
doc = new Joiner("Doc", grumpy);
grumpy.interrupt();
}
}
共享受限資源
package concurrency;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by wwh on 16-4-7.
*/
public class Counter implements Runnable {
private static int counter = 0;
public static int getCounter() {
return counter;
}
public void run() {
for (int i = 0; i < 1000000; ++i) {
counter++;
}
}
public static void test(int n) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < n; ++i) {
executorService.execute(new Counter());
}
executorService.shutdown();
}
public static void main(String[] args) throws InterruptedException {
Counter.test(10);
System.out.println(Counter.getCounter());
}
}
Lock:互斥鎖,顯示鎖對象。Lock 對象必須被顯式創建、鎖定和釋放。
ReentrantLock:可重入鎖,允許嘗試著去獲取鎖。