GitHub Page: http://blog.cloudli.top/posts/Java-線程的基本使用/
創建線程
創建線程的方式有兩種:
- 繼承
Thread類 - 實現
Runnable接口
Thread 類實現了 Runnable 接口。使用繼承 Thread 類的方式創建線程時,最大的局限是不支持多繼承。所以為了支持多繼承,應該使用實現 Runnable 接口的方式。兩種方式創建的線程在工作時是一樣的,沒有本質區別。
第一種方式,繼承 Thread 類並重寫 run() 方法:
public class Work extends Thread {
@Override
public void run() {
System.out.println("Working...");
}
}
public class Run {
public static void main(String[] args) {
Work work = new Work();
work.start();
System.out.println("End!");
}
}
運行結果可能 “End!”先輸出。在使用多線程時,運行結果與調用順序是無關的。
調用
run()方法只是普通的方法調用,不會啟動線程。如果多次調用start()方法,會拋出IllegalThreadStateException異常。
第二種方式,實現 Runnable 接口:
public class Work implements Runnable {
@Override
public void run() {
System.out.println("Working...");
}
}
public class Run {
public static void main(String[] args) {
Thread t = new Thread(new Work());
t.start();
System.out.println("End!");
}
}
這種方式與第一種在運行上沒有什么區別。其優點在於突破了單繼承的限制。
Thread 類的部分構造方法:
| 構造方法 | 說明 |
|---|---|
| Thread() | 創建一個新的線程 |
| Thread(String name) | 創建一個新的線程,並指定名稱 |
| Thread(Runnable target) | 創建一個新的線程,將 target 作為運行對象 |
| Thread(Runnable target, String name) | 將 target 作為運行對象,並指定名稱 |
| Thread(ThreadGroup group, Runnable target) | 將 target 作為運行對象,並作為線程組的一員 |
線程的方法
currentThread() 方法
currentThread() 方法返回正在被執行的線程的信息。
public class Run() {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
}
以上代碼在控制台輸出 “main“,說明該方法被名為 main 的線程調用。
import static java.lang.System.out;
public class Run {
static class Work extends Thread {
@Override
public void run() {
out.printf("%s 被調用\n", currentThread().getName());
}
}
public static void main(String[] args) {
Work t1 = new Work(),
t2 = new Work();
t1.start();
t2.start();
}
}
以上代碼運行結果:
Thread-0 被調用
Thread-1 被調用
Process finished with exit code 0
在
run()方法中可以省略Thread直接調用currentThread()方法。
isAlive() 方法
該方法判斷當前線程是否處於活動狀態。
import static java.lang.System.out;
public class Run {
static class Work extends Thread {
@Override
public void run() {
out.printf("運行中 %s\n", isAlive());
}
}
public static void main(String[] args) throws Throwable {
Work t = new Work();
out.printf("運行前: %s\n", t.isAlive());
t.start();
// 等待線程運行完成
Thread.sleep(1000);
out.printf("運行結束: %s\n", t.isAlive());
}
}
以上代碼運行結果:
運行前: false
運行中 true
運行結束: false
Process finished with exit code 0
sleep() 方法
sleep() 方法指定毫秒數讓當前線程休眠(暫停運行),該操作不會釋放鎖。
停止線程
interrupt() 方法
interrupt() 方法並不能立刻停止線程,只是在在線程中打了一個停止的標記。
import static java.lang.System.out;
public class StopThread {
static class Work extends Thread {
@Override
public void run() {
for (int i = 1; i <= 50000; i++) {
out.printf("i = %d\n", i);
}
}
}
public static void main(String[] args) throws Throwable {
Work work = new Work();
work.start();
Thread.sleep(200);
work.interrupt();
out.println("Call interrupt!");
}
}
以上代碼運行結果:
...
i = 8190
i = 8191
i = 8192
Call interrupt!
i = 8193
i = 8194
i = 8195
...
interrupt() 方法調用后,線程仍在運行。
要使用 interrupt() 方法停止線程,需要在線程中判斷中斷狀態,有兩個方法:
interrupted():測試當前線程是否是中斷狀態,執行后將狀態清除,設置為false;isInterrupted():作用同上,但是不清除狀態。
import static java.lang.System.out;
public class StopThread {
static class Work extends Thread {
@Override
public void run() {
for (int i = 1; i <= 50000; i++) {
if (isInterrupted()) {
out.println("跳出循環!");
break;
}
out.printf("i = %d\n", i);
}
}
}
public static void main(String[] args) throws Throwable {
Work work = new Work();
work.start();
Thread.sleep(200);
work.interrupt();
out.println("Call interrupt!");
}
}
以上代碼執行結果:
...
i = 8301
i = 8302
i = 8303
i = 8304
i = 8305
i = 8306
i = 8307
Call interrupt!
跳出循環!
Process finished with exit code 0
在調用 interrupt() 方法后,循環已經退出。但是這種方式只是跳出了循環,假如 for 循環外還有代碼,仍然會執行。
拋出異常停止線程
可以在判斷線程狀態為中斷時,拋出一個異常,在 catch 或 finally 塊中做中斷后的處理:
import static java.lang.System.out;
public class StopThread {
static class Work extends Thread {
@Override
public void run() {
try {
for (int i = 1; i <= 50000; i++) {
if (isInterrupted()) {
out.println("Interrupted!");
throw new InterruptedException("拋出異常!");
}
out.printf("i = %d\n", i);
}
out.println("for 循環結束!");
} catch (InterruptedException e) {
out.println(e.getMessage());
}
}
}
public static void main(String[] args) throws Throwable {
Work work = new Work();
work.start();
Thread.sleep(200);
work.interrupt();
out.println("Call interrupt!");
}
}
以上代碼將線程要執行的任務放入 try 塊中,當判斷為中斷狀態時,拋出 InterruptedException ,如果需要釋放鎖,可以在 finally 塊中執行。
也可以配合 return 來停止線程:
if (isInterrupted()) {
return;
}
暫停線程
Java 提供了 和 suspend() 方法來暫停和恢復線程,不過這兩個方法已經過期作廢了。resume()
suspend() 方法暫停線程時,不會釋放鎖。所以使用 suspend() 方法容易產生死鎖。
如果需要暫停線程,可以加入一個標記,若標記指出線程需要暫停,使用 wait() 進入等待狀態,如需要恢復,使用 notify() 喚醒。
import static java.lang.System.out;
public class StopThread {
static class Work extends Thread {
// 暫停標記
private boolean isSuspended = false;
void pause() {
isSuspended = true;
}
synchronized void wake() {
isSuspended = false;
// 喚醒
this.notify();
out.println("已喚醒!");
}
@Override
public void run() {
synchronized (this) {
try {
for (int i = 1; i <= 5000; i++) {
if (isInterrupted()) {
return;
}
if (isSuspended) {
out.println("已暫停!");
// 等待
this.wait();
}
out.printf("%s i = %d\n", getName(), i);
}
out.printf("%s end!\n", getName());
} catch (InterruptedException e) {
out.println(e.getMessage());
}
}
}
}
public static void main(String[] args) throws Throwable {
Work work = new Work();
work.start();
Thread.sleep(100);
// 暫停
work.pause();
Thread.sleep(100);
// 喚醒
work.wake();
}
}
以上代碼使用 wait() 和 notify() 暫停與恢復線程。運行結果:
...
Thread-0 i = 202
Thread-0 i = 203
Thread-0 i = 204
已暫停!
已喚醒!
Thread-0 i = 205
Thread-0 i = 206
Thread-0 i = 207
...
Thread-0 i = 4998
Thread-0 i = 4999
Thread-0 i = 5000
Thread-0 end!
Process finished with exit code 0
yield 方法
yield() 方法的作用是放棄當前的 CPU 資源,讓其他的任務去占用 CPU 執行時間。但放棄的時間不確定,有可能剛剛放棄,馬上又獲得時間片。
import static java.lang.System.currentTimeMillis;
import static java.lang.System.out;
public class Yield {
static class Work extends Thread {
@Override
public void run() {
long before = currentTimeMillis();
int sum = 0;
for (int i =1; i < 2000000; i++) {
// yield();
sum += (i + 1);
}
long after = currentTimeMillis();
out.printf("Cost: %dms\n", after - before);
}
}
public static void main(String[] args) {
new Work().start();
}
}
以上代碼不使用 yield() 方法時大概 15ms 執行完,加上后大概有 500ms。
