多线程知识点

 

1. 多线程基本概念

  1) 概念:多线程简单来说是一个程序具备同时执行多个功能的能力。在多线程中,这些功能被称为线程,每个线程都有自己的执行路径,它们可以并行(xíng)运行,同时共享程序的资源与内存。

   而在传统的单线程程序中,代码会顺序执行,一个任务完成后才会开始下一个任务。

   多线程的优势在于可以提高程序的效率和响应能力,尤其在处理一些并发性任务时特别有用。

 

  2) 进程与线程

   进程(Process):进程是计算机中正在运行的一个程序实例。它包含了程序的代码、数据和程序执行时所需要的系统资源,如内存、文件句柄等。

             每个进程都有自己独立的内存空间,一个进程无法直接访问另一个进程的内存,除非通过特殊的机制(如进程间通信)。

             进程之间是相互独立的,一个进程的崩溃通常不会影响其他进程。

      简单可以理解为进程即为计算机的一个程序,当此程序运行时,不可以运行相同的程序。

   线程(Thread):线程是进程中的一个执行单元,是程序执行的最小单位。一个进程中可以包含多个线程,这些线程共享进程的资源。

     总的来说一个进程可以包含多个线程,这些线程共享进程的资源,但每个线程有自己独立的执行路径。在多线程编程中,线程之间需要进行同步和协作,以确保数据的一致性和正确性。

 

  3) 并行与串行

  并行(Parallel):并行执行是同时执行多个任务。多个任务可以同时进行,互不干扰。加快任务的执行速度。(异步操作)

          并行执行可以显著提高程序的性能和效率,尤其是在处理大规模数据或需要大量计算的任务时。 

  串行(Serial):串行执行是按顺序一个接一个地执行任务。每个任务按照指定的顺序执行,前一个任务的完成通常是下一个任务的开始条件。(同步操作)

 

  3) CPU时间片  

   CPU时间片是操作系统中的一个概念,用于描述操作系统将CPU时间分配给各个正在运行的进程或线程的机制。简单来说,CPU时间片就是操作系统给每个进程或线程分配的一段时间,在这段时间内,该进程或线程可以使用CPU执行其任务。

   使用CPU时间片,操作系统可以实现多任务并发执行,即使在单个CPU上也可以同时运行多个进程或线程。这种方式可以提高系统的响应速度和效率,使得多个任务能够共享系统资源,但也可能导致一些调度开销。

    • 当一个进程或线程获得CPU时间片时,它就可以在CPU上执行一段时间。
    • 当这段时间结束时,操作系统会将CPU时间片分配给下一个就绪状态的进程或线程,这样就实现了进程或线程之间的切换。
    • CPU时间片的大小通常是固定的,但也可以根据系统的调度算法和策略进行调整。

  4) 计算密集型 与 IO密集型

   计算密集型(CPU-bound):长时间占用CPU。需要大量的CPU计算资源。如:视频剪辑、加密算法。

   IO密集型(IO-bound):CPU计算时间短、访问外加设备时间长input、output。

  

  5) 上下文切换  

    上下文切换,发生在多任务环境下,用于实现多个任务之间的切换和调度。当一个任务被暂停执行并切换到另一个任务时,操作系统需要保存当前任务的执行环境(称为上下文),并加载新任务的执行环境,使得新任务可以继续执行。这个过程称为上下文切换。

    发生情况:

    • 当一个任务的时间片用尽,需要切换到另一个任务时。
    • 当一个任务被阻塞(例如等待IO操作完成)时,需要切换到另一个任务。
    • 当一个任务被中断处理程序中断时,需要切换到中断处理程序。
    • 当一个任务主动放弃CPU执行权时,例如通过系统调用yield()。

 

2. 创建方式

  1) 继承 Thread() 类创建线程

  通过继承 Thread()类,重写Thread()的 run()方法,创建该类的实例,并调用start()方法,启动新线程。

 

  2) 实现runnable() 接口创建线程

    • 创建一个实现了Runnable接口的类。
    • 实现Runnable接口的run()方法,该方法包含了线程需要执行的任务代码。
    • 创建Thread类的实例,将实现了Runnable接口的类的实例作为参数传递给Thread的构造函数。
    • 调用Thread实例的start()方法,启动新线程。

 

  3) 使用Callable接口和Future接口来创建线程

  可以返回一个结果,而且可以抛出一个受检查的异常。Future接口可以用来获取Callable线程的执行结果。

 

  4) 线程池,例如用Executor框架

  使用线程池可以更有效地管理线程的生命周期和资源,而Executor框架是Java中用于管理线程池的一个重要工具。

 

  5) 使用匿名内部类  或 Lambda表达式

 

  Thread 和 Runnable的区别

  • Thread类是Java中表示线程的一个类,它直接继承自Object类,并实现了Runnable接口。
  • Runnable接口是一个功能接口,只包含了一个抽象方法run(),用于定义线程要执行的任务。
  • 使用Runnable接口创建线程可以避免单继承的限制,因为一个类可以同时实现多个接口。
  • 实现资源共享,适合多个相同的程序代码的线程去共享同一个资源。

 

3. 线程池

  1) 概念

   线程池的主要作用是线程复用、线程资源管理、控制操作系统的最大并发数,能够有效地管理线程的生命周期和资源,以保证系统高效(通过线程资源复用实现)且安全(通过控制最大线程并发数实现)地运行。

 

  3) 线程状态

  1. 新建(NEW):线程对象已经被创建,但是尚未调用start()方法,因此线程处于NEW状态。

  2. 就绪(Runnable):线程对象已经被创建并调用了start()方法,线程进入了就绪状态,等待获取CPU时间片以执行。

  3. 运行(Running):当线程获取到CPU时间片并开始执行时,线程处于运行状态。

  4. 阻塞(Blocked):线程因为某些原因被挂起,暂时无法继续执行,例如等待获取锁、等待IO操作完成等情况。在这种情况下,线程处于阻塞状态。

  5. 死亡(Terminated):线程的run()方法执行完毕或者调用了interrupt()方法中断自己时,线程处于死亡状态。在这种情况下,线程不再运行。

  注意:就绪状态和运行状态的区别在于,就绪状态表示线程已经准备好获取CPU时间片执行任务,而运行状态表示线程正在执行任务。

 

4. 线程基本方法

  1) 线程等待(Wait):wait()方法使当前线程进入等待状态,并释放对象的锁,直到其他线程调用notify()或notifyAll()方法来唤醒它。

  2) 线程睡眠(Sleep):sleep()方法使当前线程暂停执行指定的时间,进入睡眠状态,在指定时间结束后,线程重新进入就绪状态。

  3) 线程让步(Yield):yield()方法使当前线程让出CPU执行权,让其他具有相同优先级的线程有机会执行,但是让出的时间是不确定的。

  4) 线程中断(Interrupt):interrupt()方法用于中断线程的执行,当调用该方法时,线程会收到一个中断信号,可以在合适的地方进行处理。

  这些方法在多线程编程中经常用到,可以实现线程的等待、睡眠、让步和中断等操作,从而实现线程间的协作和控制。

 

5. 线程安全问题

  线程安全是指在多线程环境下,对共享资源的访问不会导致数据的不一致或不正确的情况。在多线程编程中,线程安全是一个重要的概念,因为多个线程可能同时访问共享的数据,如果不加以合适的保护和同步控制,就会出现线程安全问题,导致程序运行出现不确定的结果或者错误。

  可以简单理解为,在多线程环境中,如果多个线程同时访问同一个方法或者共享的数据,并且这个方法或数据没有被适当地保护或同步控制,就可能导致数据的不一致或不正确。

 

  解决:

  • 使用锁机制(如synchronized关键字或Lock接口)来保护共享资源,确保同一时间只有一个线程可以访问共享资源。
  • 使用线程安全的数据结构,如ConcurrentHashMap、CopyOnWriteArrayList等,它们内部已经实现了同步控制。
  • 使用volatile关键字确保共享变量的可见性。
  • 避免使用不可重入锁或者不可中断锁,以减少死锁的可能性。

 

posted @ 2024-04-10 23:13  学Java的`Bei  阅读(35)  评论(0编辑  收藏  举报