首页 > 编程语言 >二、并发编程与多线程-2.2、多线程(中)

二、并发编程与多线程-2.2、多线程(中)

时间:2024-09-09 21:21:54浏览次数:13  
标签:状态 调用 编程 start MyTask 线程 2.2 多线程 方法

2.2、多线程(中)

2.2.4、为什么启动线程不能直接调用run()方法?调用两次start()方法会有什么后果?

答:

  • 在Java中,启动线程不能直接调用run()方法的原因是,run()方法是线程的执行体,通过调用start()方法来启动线程可以创建一个新的线程并使其运行。如果直接调用run()方法,则会在当前线程中按顺序执行run()方法的代码,而不会创建新的线程
    当调用start()方法时,会在新的线程中调用run()方法,这样可以并发地执行多个线程,提高程序的执行效率。另外,调用start()方法还会执行一些必要的准备工作,包括分配新的线程栈、初始化线程的状态等。
  • 在Java中,如果尝试调用一个已经启动过的线程的start()方法,会抛出一个IllegalThreadStateException异常。这是因为线程的生命周期在调用start()方法后进入就绪状态,当线程被调度后才会进入运行状态。一旦线程进入运行状态,就不能再次调用start()方法
    如果尝试调用两次start()方法,第二次调用将会抛出IllegalThreadStateException异常,并且不会创建新的线程。这是出于线程安全和语义的考虑,避免在同一个线程中重复启动线程,导致不确定的行为和结果。因此,应该确保每个线程只调用一次start()方法,在需要重新启动线程时,应该创建一个新的线程对象来执行任务。

扩展:
Java中,线程共7种状态,但在Java API中,线程的状态只被定义成了6个枚举值,没有Running,因为Running是线程在操作系统中的状态。

  1. 新建(NEW):线程对象已经创建好了,但是还没有调用start()方法启动。
  2. 就绪(RUNNABLE):当线程对象调用start()方法后,线程进入就绪状态,表示已经准备好执行,只等待操作系统分配执行时间。
  3. 运行(RUNNING):当就绪状态的线程对象被操作系统调度后,就进入运行状态,开始执行run()方法中的任务。
  4. 阻塞(BLOCKED):当线程执行过程中遇到阻塞操作时(如IO操作、等待锁资源),线程进入阻塞状态,暂时释放CPU资源,等待阻塞条件满足后再进入就绪状态。
  5. 等待(WAITING):当线程调用wait()方法时,线程进入等待状态,直到其他线程调用notify()或notifyAll()方法唤醒该线程,才能进入就绪状态。
  6. 超时等待(TIMED WAITING):当线程调用带有超时时间的wait()、sleep()、join()方法时,线程进入超时等待状态,指定时间过后自动进入就绪状态。
  7. 终止(TERMINATED):当线程的run()方法执行完毕或因异常退出时,线程进入终止状态,不再执行任何任务。

Java线程的状态

2.2.5、谈谈你对Java线程5种状态流转原理的理解

答:

  1. 新建状态(New): 当一个线程对象被创建后,它处于新建状态。此时,线程已经分配内存,并被系统初始化。但还没有开始执行,也没有分配到CPU资源。
  2. 就绪状态(Runnable): 当线程对象调用start()方法之后,线程就进入就绪状态。此时,线程已经被系统分配到了CPU资源,但还没有开始执行。处于就绪状态的线程会进入线程调度器的等待队列中,等待系统调度以获得CPU资源。
  3. 运行状态(Running): 当线程被线程调度器选中,从就绪状态进入运行状态时,线程开始执行指定的代码逻辑。处于运行状态的线程会使用CPU资源,执行自己的逻辑。
  4. 阻塞状态(Blocked): 当线程在运行过程中,发生阻塞操作时,线程会进入阻塞状态。阻塞操作可能是因为等待某个资源的释放、等待输入输出等。处于阻塞状态的线程会释放CPU资源,并不会参与线程调度。
  5. 终止状态(Terminated): 当线程执行完成或发生异常时,线程会进入终止状态。线程执行完成后会释放所有的资源,并且不能再次进入其他状态。终止的线程对象可以被垃圾回收器回收。

2.2.6、谈谈你对线程池的理解

答:
线程池是Java中用于管理和复用线程的机制。它由线程池管理器、工作队列和线程组成。主要目的是避免频繁地创建和销毁线程,从而提高系统的性能和资源利用率。线程池会在程序启动时创建一定数量的线程,并将它们保存在线程池中,当有任务到来时,线程池会从线程池中获取一个空闲的线程来执行任务,当任务执行完成后,线程会返回线程池并等待下一个任务的到来。这样可以避免线程的频繁创建和销毁,减少了系统开销,提高了线程的复用性和并发效率。

2.2.7、Java有哪些实现线程池的方式?

答:
Java中有几种方式可以实现线程池:

  1. ThreadPoolExecutor类:这是Java提供的内置线程池实现。通过创建ThreadPoolExecutor对象,可以自定义线程池的参数,如核心线程数、最大线程数、非核心线程存活时间等。这也是最常用的实现线程池的方式
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExample {
    public static void main(String[] args) {
        // 创建一个线程池,其中包含2个核心线程,最多允许4个线程同时执行任务
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 4, 10,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));

        // 提交任务给线程池
        executor.execute(new MyTask("Task 1"));
        executor.execute(new MyTask("Task 2"));
        executor.execute(new MyTask("Task 3"));

        // 关闭线程池
        executor.shutdown();
    }

    static class MyTask implements Runnable {
        private String name;

        public MyTask(String name) {
            this.name = name;
        }

        public void run() {
            System.out.println("Task " + name + " is running.");
        }
    }
}
在上面的示例中,我们创建了一个ThreadPoolExecutor对象,并通过构造函数设置了以下参数:
核心线程数为2
最大线程数为4
空闲线程的存活时间为10秒
使用有界的阻塞队列(ArrayBlockingQueue)来存储等待执行的任务,队列的容量为10
然后,我们使用execute()方法将3个MyTask任务提交给线程池执行。最后,我们调用shutdown()方法关闭线程池。
在MyTask类中,我们实现了Runnable接口,并重写了run()方法,该方法定义了任务的具体逻辑。在本例中,run()方法简单地打印出任务的名称。
这个示例展示了如何使用ThreadPoolExecutor类手动创建和配置一个线程池。通过设置不同的参数,可以根据需求来调整线程池的行为。
  1. Executors工厂类:Java提供了Executors工厂类来创建不同类型的线程池,如FixedThreadPool固定大小线程池、CachedThreadPool缓冲线程池、ScheduledThreadPool定时线程池等。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorsExample {

    public static void main(String[] args) {
        // 创建固定大小线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);

        // 提交任务给线程池
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("Task-" + i);
            executor.execute(worker);
        }

        // 关闭线程池
        executor.shutdown();
        while (!executor.isTerminated()) {
            // 等待所有任务完成
        }
        System.out.println("Finished all tasks");
    }
}

class WorkerThread implements Runnable {

    private String taskName;

    public WorkerThread(String taskName) {
        this.taskName = taskName;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Start. Task = " + taskName);
        // 执行具体任务逻辑
        System.out.println(Thread.currentThread().getName() + " End.");
    }
}
  1. ForkJoinPool类:这是Java提供的一个特殊的线程池实现,用于支持并行计算。ForkJoinPool基于工作窃取算法,可以将一个任务拆分成多个子任务并行执行。
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class ForkJoinPoolExample {

    public static void main(String[] args) {
        // 创建ForkJoinPool
        ForkJoinPool pool = ForkJoinPool.commonPool();

        // 提交任务给ForkJoinPool
        pool.invoke(new MyTask(0, 10));

        // 关闭ForkJoinPool
        pool.shutdown();
    }
}

class MyTask extends RecursiveAction {

    private int start;
    private int end;

    public MyTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected void compute() {
        if (end - start <= 2) {
            // 执行具体任务逻辑
            System.out.println(Thread.currentThread().getName() + " Start. Range = " + start + "-" + end);
        } else {
            int mid = (start + end) / 2;
            MyTask leftTask = new MyTask(start, mid);
            MyTask rightTask = new MyTask(mid + 1, end);
            invokeAll(leftTask, rightTask);
        }
    }
}

开发时根据具体需求选择适合的方式来创建和管理线程池即可。

2.2.8、线程池是如何回收线程的?

答:
由于非核心线程是临时增加的,所以当任务处理完成后,工作线程处于空闲状态的时候,就需要回收非核心线程。
因为所有工作线程都从阻塞队列中获取要执行的任务,所以只要在一定时间内,阻塞队列中没有任何可以处理的任务,那这个线程就结束了。这个功能是通过阻塞队列里面的poll()方法来完成的。poll()方法提供了超时时间和超时时间单位两个参数,当超过指定时间没有获取到任务的时候,poll()方法返回null,从而终止当前线程,完成线程回收

扩展:默认情况下,线程池只会回收非核心线程,如果希望回收核心线程,可以设置allowCoreThreadTimeOut属性为true。不过一般情况我们不会回收核心线程,因为线程池本身就实现了线程的复用,而且核心线程在没有任务要处理的时候处于阻塞状态,并没有占用CPU资源。

标签:状态,调用,编程,start,MyTask,线程,2.2,多线程,方法
From: https://blog.csdn.net/weixin_61769871/article/details/142031290

相关文章

  • Python编程 - 进阶面向对象
    目录前言一、多态(一)多态的示例(二)多态的优势(三)总结二、静态方法(一)定义(二)特点(三)总结三、类属性(一)定义(二)类属性和实例属性的区别(三)使用场景(四)总结四、类方法(一)类方法的特点(二)定义类方法(三)使用场景(四)总结五、类对象(一)创建类对象(二)类对象的特性(三)类对象的使......
  • Linux网盘,编程者的选择,让技术为数据服务,创造无限价值!“#Linux系统编程《网盘项目》
    "Linux网盘,编程者的选择,让技术为数据服务,创造无限价值!"#Linux系统编程《网盘项目》前言预备知识一、项目功能二、程序基本框架2.1服务器程序流图2.2客户端程序流图三、程序代码解析3.1服务器代码解析3.1.1主函数代码解析3.1.2信息处理函数代码解析3.1.3获取命......
  • python编程二维码里放视频
    动植物标本制作大赛  需要制作一个关于植物标本的二维码 存放采集植物的视频 笑了 pipinstallqrcode pipinstallopencv-python-ihttps://pypi.tuna.tsinghua.edu.cn/simple  新建文件贴入代码如下:importqrcodeimportcv2#视频链接video_url="h......
  • Java并发编程15
    1、创建线程的有哪些方式继承Thread类创建线程类通过Runnable接口创建线程类通过Callable和Future创建线程通过线程池创建2、创建线程的三种方式的对比1、采用实现Runnable、Callable接口的方式创建多线程。**优势是:**线程类只是实现了Runnable接口或Calla......
  • 【高级编程】实用类详解(补充)StringBuffer类 和 StringBuilder类
    文章目录为什么使用StringBuffer?StringBuffer1.声明2.常用方法3.举例4.时间戳StringBuilderStringBufferVSStringBuilder为什么使用StringBuffer?StringBuffer是Java中的一个可变字符序列类,允许在原始对象上进行修改,而不会生成新的字符串对象。与String比较......
  • 【高级编程】实用类详解(下)万字整理Java时间日期类 JDK8新日期
    文章目录日期时间DateSimpleDateFormatCalendarJDK8新日期LocalDateTime&LocalDate&LocalTimeDateTimeFormater计算Period&DurationzonedDateTimeInstant类型转换注意事项日期时间Datejava.util.Date类:表示日期和时间。提供操作日期和时间各组成部分的方法。......
  • 【高级编程】Java流(上)字节流 InputStream OutputStream
    文章目录文件操作流输入流InputStream输出流OutputStream文件操作文件是指相关记录或放在一起的数据的集合。是一种用于存储数据的基本单位,它可以包含各种类型的信息,例如文本、图像、音频或视频。文件在计算机中通常存储在磁盘或其他存储介质上,并且每个文件都有一个......
  • 2024ICPC网络赛前总复习 2024.2.29复盘
    https://www.luogu.com.cn/problem/CF1934B此题有完全背包写法不再赘述意识到我们不可能用3个1去换一个3也不可能用2个3换一个6.。一次类推开几个for循环voidsolve(){ intlte=1e9; cin>>n; for(inti=0;i<=2;i++){ for(intj=0;j<=1;j++){ f......
  • C++ 多线程代码性能分析——Oracle Developer Studio工具教程
        最近写项目的时候,为了提升性能,把原来一些单线程的代码改成了并行运行。这里我用到的用于评估性能提升效果的工具是OracleDeveloperStudio,不过刚上手时,发现网上相关的教程和博客较少,有些功能的使用也是摸索着过来的,这一过程可谓是十分痛苦了……如今距离初次接触......
  • C++20 协程:异步编程的新纪元
    C++20引入了协程(coroutines),这是一种全新的异步编程模型,使得编写异步代码变得更加简洁和直观。本文将详细介绍C++20协程的概念、功能演变及其在实际项目中的应用。通过本文,你将了解到协程的基本原理、语法和如何利用协程来简化异步编程。1.协程的概念协程(coroutine)是......