多线程编程是现代软件开发中的重要技术,能够显著提高程序的效率和响应速度。Java 提供了丰富的多线程编程支持,使开发者可以轻松地创建和管理线程。
1. 线程的基本概念
1.1 进程与线程
- 进程:是操作系统中独立运行的程序,每个进程都有自己的内存空间和系统资源。
- 线程:是进程中的一个执行单元,一个进程可以包含多个线程。线程共享进程的内存空间和资源,但每个线程有自己的执行路径。
1.2 多线程的优点
- 提高程序响应速度:在 GUI 应用程序中,使用多线程可以保持界面的响应能力。
- 充分利用多核处理器:多线程可以在多核处理器上并行执行,提高程序性能。
- 简化异步编程:多线程使处理异步任务变得更加容易。
2. 创建线程
Java 提供了两种主要方式来创建线程:实现 Runnable
接口和继承 Thread
类。
2.1 实现 Runnable 接口
实现 Runnable
接口是创建线程的推荐方式,因为它避免了 Java 的单继承限制,并且更符合面向对象设计原则。
步骤:
- 定义一个类实现
Runnable
接口。 - 实现
Runnable
接口的run
方法。 - 创建
Thread
对象,并将Runnable
实现类的实例作为参数传递给Thread
的构造函数。 - 调用
Thread
对象的start
方法启动线程。
示例代码:
class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " - " + i);try {Thread.sleep(1000); // 休眠1秒} catch (InterruptedException e) {e.printStackTrace();}}}
}public class RunnableExample {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread1 = new Thread(myRunnable, "Thread-1");Thread thread2 = new Thread(myRunnable, "Thread-2");thread1.start();thread2.start();}
}
2.2 继承 Thread 类
继承 Thread
类是另一种创建线程的方法,但由于 Java 是单继承语言,这种方法在类需要继承其他类时不太适用。
步骤:
- 定义一个类继承
Thread
类。 - 重写
Thread
类的run
方法。 - 创建
Thread
子类的实例。 - 调用实例的
start
方法启动线程。
示例代码:
class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 5; i++) {System.out.println(getName() + " - " + i);try {Thread.sleep(1000); // 休眠1秒} catch (InterruptedException e) {e.printStackTrace();}}}
}public class ThreadExample {public static void main(String[] args) {MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();thread1.setName("Thread-1");thread2.setName("Thread-2");thread1.start();thread2.start();}
}
3. 线程管理
3.1 线程生命周期
线程有以下几种状态:
- 新建(New):线程对象被创建但未启动。
- 就绪(Runnable):线程已启动,等待 CPU 调度。
- 运行(Running):线程正在执行。
- 阻塞(Blocked):线程因某种原因暂停执行,等待资源。
- 死亡(Terminated):线程执行结束或异常终止。
3.2 线程的优先级
每个线程都有优先级,优先级较高的线程将更早被 CPU 调度执行。可以使用 setPriority
方法设置线程的优先级,范围从 1 到 10。
Thread thread = new Thread(new MyRunnable());
thread.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级
3.3 线程同步
线程同步用于解决多个线程并发访问共享资源时可能引起的数据不一致问题。Java 提供了多种同步机制。
3.3.1 同步方法
使用 synchronized
关键字修饰方法,使该方法在同一时间只能被一个线程执行。
class Counter {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}
}public class SynchronizedMethodExample {public static void main(String[] args) {Counter counter = new Counter();Runnable task = () -> {for (int i = 0; i < 1000; i++) {counter.increment();}};Thread thread1 = new Thread(task);Thread thread2 = new Thread(task);thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final count: " + counter.getCount());}
}
3.3.2 同步代码块
使用 synchronized
关键字修饰代码块,指定锁对象,以更细粒度地控制同步。
class Counter {private int count = 0;private final Object lock = new Object();public void increment() {synchronized (lock) {count++;}}public int getCount() {return count;}
}public class SynchronizedBlockExample {public static void main(String[] args) {Counter counter = new Counter();Runnable task = () -> {for (int i = 0; i < 1000; i++) {counter.increment();}};Thread thread1 = new Thread(task);Thread thread2 = new Thread(task);thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final count: " + counter.getCount());}
}
3.4 线程通信
线程通信用于在线程之间传递信息,Java 提供了 wait
、notify
和 notifyAll
方法来实现线程通信。
3.4.1 wait 和 notify
wait
:使当前线程等待,直到另一个线程调用notify
或notifyAll
方法。notify
:唤醒等待的线程中优先级最高的线程。notifyAll
:唤醒所有等待线程。
示例代码:
class SharedResource {private int value = 0;private boolean available = false;public synchronized void produce(int newValue) {while (available) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}value = newValue;available = true;System.out.println("Produced: " + value);notify();}public synchronized void consume() {while (!available) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("Consumed: " + value);available = false;notify();}
}public class WaitNotifyExample {public static void main(String[] args) {SharedResource resource = new SharedResource();Thread producer = new Thread(() -> {for (int i = 1; i <= 10; i++) {resource.produce(i);}});Thread consumer = new Thread(() -> {for (int i = 1; i <= 10; i++) {resource.consume();}});producer.start();consumer.start();}
}
4. 高级多线程技术
4.1 线程池
线程池是一组预先创建的线程,可以重复使用,避免频繁创建和销毁线程带来的性能开销。Java 提供了 java.util.concurrent
包中的 ExecutorService
接口和相关实现类来管理线程池。
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(3);Runnable task = () -> {System.out.println(Thread.currentThread().getName() + " is executing task.");try {Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {e.printStackTrace();}};for (int i = 0; i < 10; i++) {executor.submit(task);}executor.shutdown(); // 关闭线程池}
}
4.2 Callable 和 Future
Callable
接口类似于 Runnable
,但可以返回结果并且可以抛出异常。Future
用于表示异步计算的结果。
示例代码:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class CallableFutureExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(3);Callable<String> task = () -> {Thread.sleep(1000); // 模拟任务执行时间return Thread.currentThread().getName() + " has completed task.";};Future<String> future = executor.submit(task);try {System.out.println(future.get()); // 获取任务结果} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}executor.shutdown(); // 关闭线程池}
}
4.3 并发集合
Java 提供了一些线程安全的集合类,如 ConcurrentHashMap
、CopyOnWriteArrayList
和 CopyOnWriteArraySet
等。
示例代码:
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;public class ConcurrentMapExample {public static void main(String[] args) {ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();concurrentMap.put("A", 1);concurrentMap.put("B", 2);Runnable task = () -> {concurrentMap.put("C", 3);System.out.println(Thread.currentThread().getName() + " put C: " + concurrentMap.get("C"));};Thread thread1 = new Thread(task);Thread thread2 = new Thread(task);thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final map: " + concurrentMap);}
}
5. 线程安全问题与解决方案
5.1 线程安全问题
线程安全问题通常发生在多个线程并发访问共享资源时,导致数据不一致。常见问题包括竞态条件、死锁、饥饿和活锁。
5.2 解决方案
- 使用同步机制:如
synchronized
关键字、显式锁(java.util.concurrent.locks.Lock
)。 - 使用线程安全类:如
java.util.concurrent
包中的线程安全集合类。 - 避免共享资源:通过减少共享资源的使用,降低出现线程安全问题的概率。
Java 多线程编程提供了丰富的工具和类,使开发者可以灵活地创建和管理线程,从而提高程序的性能和响应能力。
通过了解线程的基本概念、创建线程的方法、线程的生命周期以及线程同步和通信机制,可以有效地解决并发编程中的各种问题。
随着 java.util.concurrent
包的引入,Java 多线程编程变得更加高效和易用,进一步提升了并发编程的开发体验。
黑马程序员免费预约咨询