Java 多线程编程:9.1 线程的创建与运行
多线程编程是Java中一个重要的特性,它允许程序同时执行多个线程,从而提高程序的效率和响应能力。在本节中,我们将深入探讨Java中线程的创建与运行,包括不同的创建方式、优缺点、注意事项以及示例代码。
1. 线程的基本概念
在Java中,线程是程序执行的最小单位。每个线程都有自己的执行路径,多个线程可以并发执行。Java通过java.lang.Thread
类和java.lang.Runnable
接口来实现多线程。
2. 创建线程的方式
Java中创建线程主要有两种方式:
2.1 继承Thread类
通过继承Thread
类来创建线程是最简单的方式。我们只需创建一个新的类,继承Thread
类,并重写run()
方法。
示例代码:
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Count: " + i);
try {
Thread.sleep(500); // 让线程休眠500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
}
}
优点:
- 简单易懂,适合初学者。
- 直接使用
Thread
类的功能。
缺点:
- Java只支持单继承,因此如果一个类已经继承了其他类,就不能再继承
Thread
类。 - 线程的功能和业务逻辑混合在一起,可能导致代码不够清晰。
注意事项:
- 在
run()
方法中不要直接调用start()
方法,否则会导致IllegalThreadStateException
异常。
2.2 实现Runnable接口
实现Runnable
接口是另一种创建线程的方式。这种方式更灵活,因为它允许我们将线程的逻辑与业务逻辑分开。
示例代码:
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - Count: " + i);
try {
Thread.sleep(500); // 让线程休眠500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableExample {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
}
}
优点:
- 可以实现多重继承,允许类继承其他类。
- 线程逻辑与业务逻辑分离,代码更清晰。
- 可以共享同一个
Runnable
实例,适合需要共享数据的场景。
缺点:
- 需要额外创建一个
Runnable
对象,稍显复杂。
注意事项:
- 共享
Runnable
实例时,需注意线程安全问题,避免数据竞争。
3. 线程的生命周期
线程的生命周期包括以下几个状态:
- 新建状态(New):线程被创建,但尚未启动。
- 就绪状态(Runnable):线程准备好运行,但尚未被CPU调度。
- 运行状态(Running):线程正在执行。
- 阻塞状态(Blocked):线程因等待某个资源而被阻塞。
- 死亡状态(Terminated):线程执行完毕或因异常终止。
4. 线程的调度
Java线程的调度是由JVM控制的,通常使用时间片轮转算法。线程的优先级可以通过setPriority(int priority)
方法设置,优先级范围为1到10,默认值为5。
示例代码:
public class ThreadPriorityExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 1 - Count: " + i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 2 - Count: " + i);
}
});
thread1.setPriority(Thread.MIN_PRIORITY); // 设置最低优先级
thread2.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级
thread1.start();
thread2.start();
}
}
5. 线程的安全性
在多线程环境中,线程安全是一个重要的问题。多个线程同时访问共享资源时,可能会导致数据不一致或程序崩溃。为了解决这个问题,可以使用以下几种方法:
- 同步(Synchronized):使用
synchronized
关键字来保证同一时间只有一个线程可以访问某个资源。 - Lock接口:使用
java.util.concurrent.locks.Lock
接口提供更灵活的锁机制。 - 原子变量:使用
java.util.concurrent.atomic
包中的原子变量类,确保操作的原子性。
示例代码(使用synchronized):
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class SynchronizedExample {
public static void main(String[] args) {
Counter counter = new Counter();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final Count: " + counter.getCount());
}
}
6. 总结
在本节中,我们详细探讨了Java中线程的创建与运行,包括继承Thread
类和实现Runnable
接口的两种方式。我们还讨论了线程的生命周期、调度和安全性等重要概念。通过示例代码,我们展示了如何在实际应用中使用这些概念。
注意事项总结:
- 选择合适的线程创建方式,考虑到代码的可读性和可维护性。
- 处理线程安全问题,避免数据竞争和不一致性。
- 理解线程的生命周期和调度机制,以便更好地控制线程的行为。
多线程编程是一个复杂但强大的工具,掌握它将极大地提升你的Java编程能力。希望本节的内容能帮助你更深入地理解Java中的多线程编程。