在Java中,多线程编程是一种重要的编程模型,它允许程序同时执行多个任务,从而提高了程序的执行效率和响应速度。
一、基本概念
-
进程与线程:进程是系统分配资源的基本单位,它包含了程序执行所需的资源,如代码、数据、系统资源等。而线程是CPU调度的基本单位,它是进程中的一个执行单元,负责执行进程中的一段代码。一个进程可以包含多个线程,这些线程共享进程的资源。
-
并发与并行:并发是指多个任务在同一时间段内交替执行,而并行是指多个任务在同一时刻同时执行。在多核CPU的系统中,可以实现真正的并行执行,而在单核CPU的系统中,只能实现并发执行。
二、线程的三种创建方式
1.继承Thread类
通过继承Thread类并重写其run()方法,可以创建自定义的线程类。然后,创建该线程类的实例,并调用其start()方法即可启动线程。
优点:
- 代码简单明了,易于理解。
- 可以直接调用Thread类中的方法,如
start()
,run()
,interrupt()
,isInterrupted()
等。
缺点:
- Java不支持多重继承,如果继承了Thread类,那么就不能再继承其他类。
- 线程任务与代码耦合度高,不利于代码的复用和扩展。
示例:
运行结果
2.实现Runnable接口
通过实现Runnable接口并重写其run()方法,也可以创建线程。这种方式更加灵活,因为Java不支持多继承,但可以实现多个接口。通常,我们会将线程的任务逻辑封装在一个实现了Runnable接口的类中,然后将其作为参数传递给Thread类的构造函数来创建线程。
优点:
- 避免了单继承的局限性,因为Java类可以实现多个接口。
- 任务代码与线程代码分离,有利于代码的复用和扩展。
- 可以通过共享Runnable实例来创建多个线程执行相同的任务。
缺点:
- 编写代码时稍微复杂一些,需要额外创建Thread对象。
示例:
运行结果
3.使用Callable与Future接口
创建一个实现Callable
接口的类,这个类中的call()
方法会包含线程要执行的代码。然后,你可以创建一个Thread
对象,并将Callable
对象包装在FutureTask
中作为Thread
的目标来执行。FutureTask
实现了Future
接口,允许你在主线程中等待Callable
任务的完成并获取其结果。
优点:
- Callable接口可以返回执行结果,并且可以声明抛出异常。
- 通过Future接口可以获取Callable执行结果的状态,并且可以获取执行的结果。
缺点:
- 相比Runnable接口,代码稍微复杂一些。
示例:
开启一个线程,计算0到100的和,并将结果输出到控制台。
运行结果
三、线程的生命周期
线程的生命周期主要可以分为以下几个阶段:
- 新建状态:当线程对象被创建,但尚未调用其start()方法时,线程处于新建状态。此时,线程已经完成了初始化,但还没有开始执行。
- 就绪状态:当线程调用start()方法后,线程进入就绪状态。这意味着线程已经做好了运行准备,等待CPU的调度。
- 运行状态:当线程获得CPU时间片并开始执行时,线程进入运行状态。此时,线程正在执行其任务。
- 阻塞状态:线程因为某种原因(如等待I/O操作完成、等待获取锁等)暂时放弃CPU使用权,进入阻塞状态。阻塞状态是线程生命周期中的一个重要环节,它允许线程在必要时暂停执行,以便其他线程或系统资源可以得到使用。
- 等待状态:当线程需要等待某些条件满足时(如等待其他线程的通知或某个变量的值发生变化),线程会进入等待状态。
- 计时等待状态:这是等待状态的一种特殊情况,线程在等待时会设置一个超时时间。如果在超时时间内条件没有满足,线程会自动醒来并继续执行。
- 终止状态:当线程完成了任务或因为异常等原因退出时,线程进入终止状态。这标志着线程生命周期的结束。
四、线程同步与通信
多线程编程中,线程同步与通信是一个重要的问题。由于多个线程可能同时访问共享资源,如果没有适当的同步机制,就可能导致数据不一致或其他不可预测的问题。
Java提供了多种同步机制:
- synchronized关键字:可以用来修饰方法或代码块,确保同一时刻只有一个线程能够执行被修饰的代码。
-
wait/notify/notifyAll方法:这些方法用于线程间的通信。wait()方法使当前线程等待,直到其他线程调用该对象的notify()或notifyAll()方法。notify()方法唤醒在此对象监视器上等待的单个线程,而notifyAll()方法则唤醒在此对象监视器上等待的所有线程。
-
Lock接口:Java 5引入了Lock接口及其实现类,提供了比synchronized更灵活的锁机制。Lock接口的实现类包括ReentrantLock等。
通过合理使用Java提供的同步和通信机制,可以编写出高效且稳定的多线程程序。
五、总结
Java多线程编程是一个强大而复杂的特性,它允许我们充分利用多核CPU的优势,提高程序的执行效率和响应速度。然而,多线程编程也带来了线程安全和并发控制等问题。需要深入理解和掌握Java多线程的相关概念和关键技术,才能编写出高效、稳定的多线程程序。