Java 多线程核心概念与实战用法详解
Java 多线程是并发编程的基础,掌握其创建方式、生命周期管理、线程安全控制以及线程间通信机制,对于构建高性能应用至关重要。本文将系统梳理这些核心概念并提供代码示例。
如何高效创建 Java 线程
在 Java 中,创建新执行线程主要有两种方式。无论采用哪种方式,最终都必须实例化 Thread 类或其子类来启动线程。以下是 Thread 类的常用构造函数:
public Thread( );public Thread(Runnable target);public Thread(String name);public Thread(Runnable target, String name);public Thread(ThreadGroup group, Runnable target);public Thread(ThreadGroup group, String name);public Thread(ThreadGroup group, Runnable target, String name);public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
第1步:继承 Thread 类覆盖 run 方法
这是最直接的线程创建方式。通过创建一个继承自 Thread 的类,并重写 run() 方法来定义线程任务。启动时需调用 start() 方法而非直接调用 run(),否则会阻塞在主线程中。
public class ThreadDemo1 {
public static void main(String[] args){
Demo d = new Demo();
d.start(); // 启动线程
for(int i=0;i<60;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
class Demo extends Thread{
public void run(){
for(int i=0;i<60;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
}
第2步:实现 Runnable 接口
这种方式将任务逻辑(Runnable)与执行主体(Thread)分离,更符合面向对象设计原则,也避免了单继承的限制。需先创建实现了 Runnable 接口的对象,再将其作为目标传入 Thread 构造函数。
public class ThreadDemo2 {
public static void main(String[] args){
Demo2 d =new Demo2();
Thread t = new Thread(d); // 绑定任务到线程
t.start();
for(int x=0;x<60;x++){
System.out.println(Thread.currentThread().getName()+x);
}
}
}
class Demo2 implements Runnable{
public void run(){
for(int x=0;x<60;x++){
System.out.println(Thread.currentThread().getName()+x);
}
}
}
线程的生命周期与控制
线程从创建到销毁经历多个状态。理解并正确控制这些状态是避免死锁和资源浪费的关键。
- 新建 (New):线程对象已创建但尚未启动。
- 运行 (Running/Runnable):调用
start()后进入就绪队列等待 CPU 调度,获配资源后执行run()。 - 阻塞/挂起 (Blocked/Waiting/Sleeping):因等待资源、睡眠或调用
wait()而暂停。注意:suspend()和resume()已废弃,因其易导致死锁,请使用wait()/notify()或Lock机制替代。 - 终止 (Terminated):任务执行完毕或被强制中断。
关键状态控制方法:
start():启动线程。run():线程执行的入口,不可直接调用作为启动手段。sleep(long millis):静态方法,使当前线程休眠指定时间,不释放锁。需处理InterruptedException。若设置纳秒参数,JVM 可能降级为毫秒精度。interrupt():中断线程,通常用于唤醒处于睡眠或等待状态的线程,抛出异常供捕获处理。isAlive():判断线程是否存活(即已启动且未终止)。
线程终止的三种方式:
- 正常退出(推荐):通过标志位(如 boolean flag)控制循环条件,使
run()方法自然结束。 - 强制停止(不推荐):
stop()方法会立即终止线程并释放所有锁,可能导致数据不一致,官方已废弃。 - 中断响应(推荐):使用
interrupt()发送中断信号,线程在适当时机捕获异常或检查isInterrupted()状态以优雅退出。
Join 方法的作用:
join() 用于确保当前线程等待另一个线程终止后再继续执行。这常用于父线程需要获取子线程计算结果的情况,保证执行顺序的同步性。
解决多线程安全问题
当多个线程同时操作共享数据时,可能出现数据竞争。解决办法是确保同一时刻只有一个线程执行操作共享数据的代码块,即“同步”。Java 提供了两种主要同步机制:
第1步:使用同步代码块
通过 synchronized(lockObject) {...} 块来保护关键代码。锁对象可以是任意非空对象,通常建议使用专门的私有对象作为锁,以避免与外部逻辑冲突。以下示例演示了多窗口售票场景中使用同步代码块防止超卖:
class Ticket implements Runnable{
private int ticket =400;
public void run(){
while(true){
synchronized (this) { // 使用 this 作为锁
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket>0)
System.out.println(Thread.currentThread().getName()+"---卖出"+ticket--);
else
break;
}
}
}
}
第2步:使用同步方法
直接在方法签名上添加 synchronized 关键字。如果是实例方法,锁默认为 this;如果是静态方法,锁默认为 Class 对象。此方式代码更简洁,适用于整个方法体都需要保护的场景。
class Ticket implements Runnable{
private int ticket = 4000;
public synchronized void saleTicket(){ // 同步方法
if(ticket>0)
System.out.println(Thread.currentThread().getName()+"卖出了"+ticket--);
}
public void run(){
while(true){
saleTicket();
}
}
}
线程间的通信:等待与唤醒
线程间通信通常涉及生产者-消费者模式。核心机制是使用 wait() 和 notify()/notifyAll()。必须注意,这两个方法必须在 synchronized 块内调用,因为它们是 Object 类的方法,且依赖于特定的监视器锁。
第1步:基础通信与问题发现
若无同步保护,线程访问共享对象属性时会出现脏读(读到一半的数据)。加入简单的 synchronized 可解决数据完整性问题,但在无锁竞争中可能轮流执行,无法体现生产消费的协作特性(即库存满时生产者等待,库存空时消费者等待)。
第2步:引入 wait/notify 机制
通过标志位和等待/唤醒机制,实现线程间的协调。只有当条件满足时才执行操作,否则线程进入等待状态释放锁,让其他线程执行。
class Goods{
private String name;
private int num;
// 生产方法:同步,需加锁
public synchronized void produce(String name){
// 如果库存已满(标记为true),等待消费
while(this.isFull()) {
try { wait(); } catch (InterruptedException e) { e.printStackTrace(); }
}
// 生产商品...
this.name = name + "编号:" + num++;
System.out.println(Thread.currentThread().getName() + "生产了: " + this.name);
// 更新状态为已生产,并唤醒所有等待者(主要是消费者)
notifyAll();
}
public synchronized void consume(){
// 如果库存为空(标记为false),等待生产
while(!this.hasStock()) {
try { wait(); } catch (InterruptedException e) { e.printStackTrace(); }
}
// 消费商品...
System.out.println(Thread.currentThread().getName() + "消费了: " + this.name);
// 更新状态为空,并唤醒所有等待者(主要是生产者)
notifyAll();
}
}
在上述改进的生产消费模型中,while 循环用于处理虚假唤醒(Spurious Wakeup),确保唤醒后再次检查条件是否符合。使用 notifyAll() 比 notify() 更安全,能避免特定线程一直未被唤死的死锁风险。通过这种协作,生产者和消费者能够根据库存状态自动调节执行节奏,提高系统效率。
总结
Java 多线程编程涉及复杂的并发控制。从基础的线程创建,到生命周期管理,再到关键的线程安全和线程间通信,每一步都需要谨慎处理。推荐使用 Runnable 接口解耦任务与执行,利用 synchronized 或 java.util.concurrent 包中的高级工具(如 ReentrantLock、Semaphore)来解决同步问题,并妥善运用 wait/notify 实现高效的线程协作。以上就是Java多线程详解及用法的详细内容,更多关于Java并发编程的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













