Java 的多線程是一種允許在一個程式中同時運行多個線程的技術。每個線程是獨立的執行路徑,可以併發執行,從而提高程式的效率和響應能力。 1. 線程基礎 Java 中的線程可以通過繼承 Thread 類或實現 Runnable 介面來創建和管理。 1.1 繼承 Thread 類 class MyThr ...
Java 的多線程是一種允許在一個程式中同時運行多個線程的技術。每個線程是獨立的執行路徑,可以併發執行,從而提高程式的效率和響應能力。
1. 線程基礎
Java 中的線程可以通過繼承 Thread
類或實現 Runnable
介面來創建和管理。
1.1 繼承 Thread
類
class MyThread extends Thread {
public void run() {
System.out.println("Thread is running...");
}
}
public class TestThread {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // 啟動線程
}
}
run()
方法包含線程執行的代碼,而 start()
方法用於啟動新線程。
1.2 實現 Runnable
介面
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running...");
}
}
public class TestRunnable {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
t1.start(); // 啟動線程
}
}
通過實現 Runnable
介面的方式可以更靈活地共用資源。
2. 線程的生命周期
線程在其生命周期中經歷以下狀態:
- 新建(New): 線程對象被創建,但還沒有調用
start()
方法。 - 就緒(Runnable): 線程對象調用了
start()
方法,等待 CPU 調度。 - 運行(Running): 線程獲得 CPU,開始執行
run()
方法的代碼。 - 阻塞(Blocked): 線程因為某種原因(如等待資源、睡眠)被掛起。
- 死亡(Dead): 線程執行完
run()
方法,或者因異常退出。
3. 線程式控制制
Java 提供了一些方法來控制線程的執行:
sleep(long millis)
:讓當前線程睡眠指定的毫秒數。join()
:等待該線程終止,也就是說等待當前的線程結束, 才會繼續執行下麵的代碼yield()
:暫停當前線程,讓出 CPU 給其他線程。interrupt()
:中斷線程。
4. 線程同步
多線程程式中可能會出現多個線程同時訪問共用資源的情況,導致數據不一致的問題。為瞭解決這個問題,可以使用同步技術。
4.1. ReentrantLock
ReentrantLock
提供了比 synchronized
更加靈活的鎖機制,可以顯式地鎖定和解鎖。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
4.2. ReadWriteLock
ReadWriteLock
提供了一對鎖,一個用於讀操作,一個用於寫操作。這允許多個讀線程同時訪問共用資源,但在寫線程訪問時會獨占鎖。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private int count = 0;
public void increment() {
lock.writeLock().lock();
try {
count++;
} finally {
lock.writeLock().unlock();
}
}
public int getCount() {
lock.readLock().lock();
try {
return count;
} finally {
lock.readLock().unlock();
}
}
}
4.3 synchronized
在方法前使用 synchronized
關鍵字,確保同一時間只有一個線程可以執行該方法。
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
4.4 同步塊
同步塊可以更靈活地控制需要同步的代碼塊,而不是整個方法。
class Counter {
private int count = 0;
public void increment() {
synchronized (this) {
count++;
}
}
public int getCount() {
return count;
}
}
4.5 註意事項
下麵代碼中的寫法, 不能保證同一時間只有一個線程可以執行該方法。
因為 4.3 和 4.4 中 synchronized
的寫法是根據 this
來保證同一時間只有一個線程可以執行, 但是他們的 this
是不同的。(把 "需要替換的" 換成註釋上的就可以 "保證同一時間只有一個線程可以執行")
class Worker implements Runnable {
public static int cnt = 0;
@Override
public synchronized void run() {
for (int i = 0; i < 100000; i ++) {
cnt ++;
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
// Worker worker = new Worker();
// Thread t1 = new Thread(worker);
// Thread t2 = new Thread(worker);
Thread t1 = new Thread(new Worker()); // 需要替換
Thread t2 = new Thread(new Worker()); // 需要替換
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Worker.cnt);
}
}
當然, 4.4 的代碼也可以寫成下麵這樣,這樣他就是根據 lock
這個對象來保證同步的, java中的對象都可以當作lock
class Counter {
public static final Object lock = new Object();
private int count = 0;
public void increment() {
synchronized (lock ) {
count++;
}
}
public int getCount() {
return count;
}
}
5. 線程通信
Java 提供了 wait()
, notify()
, notifyAll()
方法來實現線程間的通信。
wait()
:讓當前線程等待,直到其他線程調用notify()
或notifyAll()
。notify()
:喚醒一個正在等待的線程。notifyAll()
:喚醒所有正在等待的線程。
6. 線程池
使用線程池可以有效地管理和復用線程,減少創建和銷毀線程的開銷。Java 提供了 Executor
框架來管理線程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestThreadPool {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executor.execute(new MyRunnable());
}
executor.shutdown();
}
}
7. 高級線程工具
Java 提供了很多高級線程工具,如 Semaphore
, CountDownLatch
, CyclicBarrier
等,用於複雜的線程協調和同步。
7.1 Semaphore
信號量控制同時訪問特定資源的線程數量。
import java.util.concurrent.Semaphore;
public class TestSemaphore {
private static final Semaphore semaphore = new Semaphore(3);
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new Task()).start();
}
}
static class Task implements Runnable {
public void run() {
try {
semaphore.acquire();
System.out.println("Thread " + Thread.currentThread().getName() + " is accessing the resource.");
Thread.sleep(2000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
7.2 CountDownLatch
允許一個或多個線程等待,直到在其他線程中執行的一組操作完成。
import java.util.concurrent.CountDownLatch;
public class TestCountDownLatch {
private static final int THREAD_COUNT = 3;
private static final CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(new Task()).start();
}
try {
latch.await(); // 主線程等待所有子線程完成
System.out.println("All threads have finished.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static class Task implements Runnable {
public void run() {
System.out.println("Thread " + Thread.currentThread().getName() + " is working.");
latch.countDown(); // 計數器減一
}
}
}
結論
Java 多線程是一個強大且複雜的技術,需要深入理解和小心使用,以避免潛在的併發問題和死鎖情況。通過合理地利用線程同步、線程通信和線程池等工具,可以編寫高效且安全的多線程應用程式。