深入探讨Java多线程

我的主页: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资源。

@Override
public 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线程已进入终止状态。

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值