多线程(一)

  1. 什么是线程?线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程中,是进程中的实际执行单元。线程和进程类似,但线程是进程中的一个独立运行的部分,是进程中的轻量级进程。
  2. 多线程是什么?多线程(Multi-threading)是指在一个进程中同时运行多个线程,每个线程执行其自己的任务。多线程可以让程序在同一时刻执行多个任务,从而提高程序的执行效率和系统的资源利用率。

多线程用在哪儿?有什么好处?

  1. 用户界面(UI)响应性:在图形用户界面应用程序中,多线程可以用来确保UI的流畅响应。例如,一个线程负责处理用户输入和更新界面,而另一个线程在后台执行耗时的任务,如文件下载或数据处理,这样用户就不会因为长时间等待而感到应用程序无响应。
  2. 服务器并发处理:在服务器端编程中,多线程可以用来处理多个客户端的并发请求。每个客户端的请求可以由一个独立的线程来处理,这样可以提高服务器的吞吐量和响应速度。
  3. 并行计算:在需要大量计算的场景中,如科学计算、图像处理、加密解密等,多线程可以用来将任务分解成多个子任务,每个子任务由一个线程并行执行,从而加快计算速度。
  4. 异步I/O操作:在进行文件读写、网络通信等I/O密集型操作时,多线程可以用来实现异步操作,即在等待I/O操作完成的同时,其他线程可以继续执行其他任务,从而提高程序的效率。
  5. 多核处理器利用:随着多核处理器的普及,多线程编程可以更好地利用多核处理器的计算能力,通过将任务分配到不同的核心上并行执行,提高整体的处理速度。
  6. 游戏开发:在游戏开发中,多线程可以用来处理游戏逻辑、图形渲染、物理模拟、网络通信等多个方面,以提供更流畅的游戏体验。
  7. 实时系统:在实时系统中,多线程可以用来确保关键任务的及时执行,同时处理其他非关键任务,以满足系统的实时性要求。
  8. 数据处理和分析:在大数据处理和分析中,多线程可以用来并行处理数据集,加快数据处理的速度,提高分析效率。

多线程编程虽然能够提高程序的性能和响应性,但也需要开发者注意线程同步、资源竞争、死锁等问题,以确保程序的稳定性和正确性。

  1. 如何在程序中创建线程?

有四种方法创建新的执行线程:

        1)将类声明为Thread的子类(继承),该子类应该覆盖(重写)类Threadrun方法,然后可以分配和启动子类的实例【继承Thread + 重写un方法】

优点:代码简单

      缺点:只是继承Thread类,无法继承其他类,不利于功能的扩展。

       2)声明一个实现接口Runnable接口的类,该类然后实现run方法。然后可以分配和启动子类的实例。【实现Runnable + 重写run方法 + 把runnable任务交给Thread实例】

优点:任务类只是实现接口,仍然可以继承其他类、实现其他接口,扩展性强。

缺点:多创建一个任务类对象(其实无所谓)      

 3)声明一个实现Callable接口的类。

4)使用线程池 

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadPoolExample {

    public static void main(String[] args) {
        // 创建一个固定大小的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        // 创建一个 Runnable 任务
        Runnable myRunnable = () -> {
            System.out.println("Task is running in thread: " + Thread.currentThread().getName());
        };

        // 提交任务给线程池
        executorService.submit(myRunnable);

        // 创建一个 Callable 任务并获取 Future 对象
        Callable<String> callableTask = () -> {
            // 模拟一些工作
            Thread.sleep(1000);
            return "Callable Task Result";
        };
        Future<String> future = executorService.submit(callableTask);

        try {
            // 获取 Callable 任务的结果
            String result = future.get(); // 这会阻塞,直到任务完成
            System.out.println("Result from Callable: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executorService.shutdown();
        }
    }
}
  1. 多线程注意事项:

    1)启动线程必须是调用start方法,不是调用run方法(否则,整个程序本质上就是一个实例调用其方法,直到程序完成,只有一条主线程在推进)

    2)子线程一定要在主线程之前启动。(否则,整个程序相当于是单线程的一个效果)

  1. 对比四种线程创建方法的不同点:上面的总结

这三种方法的主要区别如下:

  1. 继承Thread:这种方法简单易用,但有以下缺点:
    • 不能多继承,因为Java不支持多继承,如果需要继承其他类,则不能继承Thread类。
    • 线程类的子类对象可以直接当作线程使用,但是这种做法可能会导致线程与具体的任务过于耦合,不利于代码的重用和扩展。
  2. 实现Runnable接口:这种方法可以避免继承Thread类的缺点,可以在多个线程中重用同一个Runnable对象,更加灵活和可扩展
  3. CallableFuture:这种方法可以获取线程执行结果,并且支持捕获线程执行过程中的异常。但是需要注意,FutureTask对象只能使用一次,如果需要重复获取线程执行结果,则需要创建多个FutureTask对象。

总的来说,选择哪种方法取决于具体的应用场景和需求。如果仅仅需要执行一些简单的任务,可以使用继承Thread类的方法;如果需要更灵活和可扩展的线程对象,可以使用实现Runnable接口的方法;如果需要获取线程执行结果,可以使用Callable和Future方法。

解惑???为什么FutureTask 和runnable可以作为线程的目标,但是callable不行?

FutureTaskRunnable 可以直接作为线程的目标,但是 Callable 不行,原因在于它们的设计和接口定义。

1. Runnable 接口Runnable 是一个接口,它有一个 run() 方法,这个方法是线程执行的入口点。当你创建一个实现了 Runnable 接口的类的实例,并将该实例传递给 Thread 对象时,Threadstart() 方法会调用 Runnable 实例的 run() 方法。

class MyRunnable implements Runnable {
    public void run() {
        // 执行的代码
    }
}

 2. FutureTask 类FutureTask 是一个实现了 RunnableFuture 接口的类,它同时实现了 Runnable 接口和 Future 接口。因此,FutureTask 可以作为 Thread 的目标,并且提供了获取异步计算结果的能力。

FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask); 
thread.start();

3. Callable 接口Callable 是一个接口,它有一个 call() 方法,这个方法是线程执行的入口点,但它与 Runnablerun() 方法不同,call() 可以返回一个值,并且可以抛出异常(包括检查型异常)。然而,Callable 本身并没有实现 Runnable 接口,因此不能直接传递给 Thread 对象。

要让 Callable 作为线程的目标,你需要将它封装在一个 FutureTask 或者其他实现了 Runnable 接口的包装器中。FutureTask 提供了一个接受 Callable 对象的构造函数,这样 Callablecall() 方法就可以被 FutureTaskrun() 方法调用。

Callable<Integer> callable = () -> { 
// 执行的代码,并返回结果 
return 123;
 }; 
FutureTask<Integer> futureTask = new FutureTask<>(callable);
 Thread thread = new Thread(futureTask); thread.start();

总结来说,在Java中,线程接收的对象需要实现 Runnable 接口或者继承 ThreadRunnableFutureTask 可以直接作为线程的目标,因为它们实现了 Runnable 接口,而 Callable 需要通过 FutureTask 或类似的包装器间接地作为线程的目标,因为 Callable 没有实现 Runnable 接口,但它提供了异步计算结果的获取能力。这种设计允许 Callable 任务既可以执行,也可以返回结果,而 Runnable 任务只能执行。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值