我的主页:2的n次方_
1. 多线程的概念
多线程是指在同一个程序中同时执行多个线程的技术。线程是操作系统能够独立调度和执行的最小单位。在Java中,线程由Thread
类来表示,所有的线程都是通过这个类或其子类来创建和控制的。通过合理的多线程设计,程序可以在一个处理器上同时执行多个任务,极大地提高了程序的执行效率。
2. Java中实现多线程的方式
2.1 继承Thread类
通过继承Thread
类并重写其run()
方法,我们可以创建一个新的线程。run()
方法包含了线程在启动后要执行的代码。
class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " - " + i); } }}public class Main { public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); thread1.start(); thread2.start(); }}
在这个例子中,我们创建了两个线程,每个线程都会执行run()
方法中的代码。start()
方法用于启动线程,而不是直接调用run()
方法。
2.2 实现Runnable接口
相比继承Thread
类,实现Runnable
接口更为灵活,因为Java支持单继承但允许实现多个接口。
class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " - " + i); } }}public class Main { public static void main(String[] args) { Thread thread1 = new Thread(new MyRunnable()); Thread thread2 = new Thread(new MyRunnable()); thread1.start(); thread2.start(); }}
在这个例子中,我们创建了两个线程,并分别传入实现了Runnable
接口的对象。Thread
类的构造函数接受一个Runnable
对象,这使得线程的创建更加灵活。
2.3 使用Callable和Future
Runnable
接口的run()
方法无法返回结果,而Callable
接口的call()
方法则可以返回结果,并且可以抛出异常。配合Future
接口,Callable
可以用来处理需要返回值的任务。
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;class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i < 5; i++) { sum += i; } return sum; }}public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); Future<Integer> future = executor.submit(new MyCallable()); try { Integer result = future.get(); System.out.println("Sum: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown(); }}
在这个例子中,我们通过Callable
实现了一个简单的求和任务,并使用ExecutorService
来管理线程执行。通过Future
对象,我们可以获取线程执行的结果。
2.4 线程池的使用
线程池是一种管理多个线程的工具,可以有效地减少线程的创建和销毁开销,提高资源利用率。Java通过ExecutorService
提供了多种创建线程池的方式。
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " - " + i); } }}public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); for (int i = 0; i < 5; i++) { executor.submit(new MyRunnable()); } executor.shutdown(); }}
在这个例子中,我们创建了一个固定大小的线程池,并向线程池提交了多个任务。线程池会合理分配线程来执行这些任务。
3. 线程的生命周期
Java线程的生命周期包括多个状态,从线程的创建到终止,线程会经历不同的状态转换。理解线程的生命周期对于编写高效的多线程应用程序至关重要。
3.1 新建状态(New)
当一个线程对象被创建时,它处于新建状态。此时,线程对象已经存在,但还没有调用start()
方法来启动线程。新建状态是线程生命周期的起始点,在这个状态下,线程还没有分配任何CPU资源。
Thread thread = new Thread(() -> { // Thread code here});
3.2 就绪状态(Runnable)
当调用start()
方法后,线程从新建状态进入就绪状态。在这个状态下,线程已经具备了运行的条件,等待操作系统调度器分配CPU时间片来执行。需要注意的是,在Java中,“就绪状态”并不意味着线程正在运行,而是等待被调度。
thread.start(); // Thread moves to the Runnable state
线程调用start()
后,立即进入就绪状态。操作系统的线程调度器会根据线程的优先级等条件,将就绪状态的线程分配到CPU上执行。
3.3 运行状态(Running)
当线程获得CPU时间片,开始执行run()
方法中的代码时,线程进入运行状态。这是线程生命周期中唯一实际执行代码的状态。线程在这个状态下进行任务处理,直到被操作系统中断或自主放弃CPU资源。
@Overridepublic void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " - " + i); }}
在这个例子中,线程进入运行状态并执行run()
方法中的代码。运行状态通常非常短暂,因为操作系统会定期中断线程,以允许其他就绪线程获得执行机会。
3.4 阻塞状态(Blocked)
线程在执行过程中可能会因为某些原因(如等待资源、I/O操作、线程同步)而暂停运行,进入阻塞状态。在这个状态下,线程暂时放弃了CPU资源,直到满足特定条件(如获取锁或完成I/O操作),线程才能重新进入就绪状态。
阻塞状态可以进一步细分为以下几种:
等待阻塞(Waiting Blocked):线程通过调用wait()
方法进入等待队列,等待其他线程通过notify()
或notifyAll()
方法唤醒它。
超时等待阻塞(Timed Waiting Blocked):线程通过调用sleep(long millis)
、join(long millis)
、wait(long millis)
等方法进入此状态,线程会在指定时间后自动苏醒,或在满足条件时被唤醒。
同步阻塞(Synchronized Blocked):线程试图获取对象的同步锁,但该锁已被其他线程持有,此时线程进入同步阻塞状态。
synchronized (lock) { // Thread blocks if lock is held by another thread}
在这个例子中,线程试图获取锁对象lock
,如果该锁被其他线程持有,当前线程将进入同步阻塞状态,直到锁可用。
3.5 终止状态(Terminated)
当线程的run()
方法执行完毕或抛出未捕获的异常时,线程进入终止状态。此时,线程的生命周期结束,不能再重新启动。线程进入终止状态后,系统会释放线程所占用的所有资源。
thread.join(); // Waits for thread to die
调用join()
方法后,主线程会等待thread
线程终止后才继续执行。此时,thread
线程已进入终止状态。