多线程是啥

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程是啥相关的知识,希望对你有一定的参考价值。

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。
在计算机编程中,一个基本的概念就是同时对多个任务加以控制。许多程序设计问题都要求程序能够停下手头的工作,改为处理其他一些问题,再返回主进程。可以通过多种途径达到这个目的。最开始的时候,那些掌握机器低级语言的程序员编写一些“中断服务例程”,主进程的暂停是通过硬件级的中断实现的。尽管这是一种有用的方法,但编出的程序很难移植,由此造成了另一类的代价高昂问题。中断对那些实时性很强的任务来说是很有必要的。但对于其他许多问题,只要求将问题划分进入独立运行的程序片断中,使整个程序能更迅速地响应用户的请求 。
最开始,线程只是用于分配单个处理器的处理时间的一种工具。但假如操作系统本身支持多个处理器,那么每个线程都可分配给一个不同的处理器,真正进入“并行运算”状态。从程序设计语言的角度看,多线程操作最有价值的特性之一就是程序员不必关心到底使用了多少个处理器。程序在逻辑意义上被分割为数个线程;假如机器本身安装了多个处理器,那么程序会运行得更快,毋需作出任何特殊的调校。根据前面的论述,大家可能感觉线程处理非常简单。但必须注意一个问题:共享资源!如果有多个线程同时运行,而且它们试图访问相同的资源,就会遇到一个问题。举个例子来说,两个线程不能将信息同时发送给一台打印机。为解决这个问题,对那些可共享的资源来说(比如打印机),它们在使用期间必须进入锁定状态。所以一个线程可将资源锁定,在完成了它的任务后,再解开(释放)这个锁,使其他线程可以接着使用同样的资源。
参考技术A 1.多线程的概念?
  说起多线程,那么就不得不说什么是线程,而说起线程,又不得不说什么是进程。
  进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
  进程可以简单的理解为一个可以独立运行的程序单位。它是线程的集合,进程就是有一个或多个线程构成的,每一个线程都是进程中的一条执行路径。
  那么多线程就很容易理解:多线程就是指一个进程中同时有多个执行路径(线程)正在执行。
  为什么要使用多线程?
  1.在一个程序中,有很多的操作是非常耗时的,如数据库读写操作,IO操作等,如果使用单线程,那么程序就必须等待这些操作执行完成之后才能执行其他操作。使用多线程,可以在将耗时任务放在后台继续执行的同时,同时执行其他操作。
  2.可以提高程序的效率。
  3.在一些等待的任务上,如用户输入,文件读取等,多线程就非常有用了。
  缺点:
  1.使用太多线程,是很耗系统资源,因为线程需要开辟内存。更多线程需要更多内存。
  2.影响系统性能,因为操作系统需要在线程之间来回切换。
  3.需要考虑线程操作对程序的影响,如线程挂起,中止等操作对程序的影响。
  4.线程使用不当会发生很多问题。
  总结:多线程是异步的,但这不代表多线程真的是几个线程是在同时进行,实际上是系统不断地在各个线程之间来回的切换(因为系统切换的速度非常的快,所以给我们在同时运行的错觉)。
2.多线程与高并发的联系。
  高并发:高并发指的是一种系统运行过程中遇到的一种“短时间内遇到大量操作请求”的情况,主要发生在web系统集中大量访问或者socket端口集中性收到大量请求(例如:12306的抢票情况;天猫双十一活动)。该情况的发生会导致系统在这段时间内执行大量操作,例如对资源的请求,数据库的操作等。如果高并发处理不好,不仅仅降低了用户的体验度(请求响应时间过长),同时可能导致系统宕机,严重的甚至导致OOM异常,系统停止工作等。如果要想系统能够适应高并发状态,则需要从各个方面进行系统优化,包括,硬件、网络、系统架构、开发语言的选取、数据结构的运用、算法优化、数据库优化……。
  而多线程只是在同/异步角度上解决高并发问题的其中的一个方法手段,是在同一时刻利用计算机闲置资源的一种方式。
  多线程在高并发问题中的作用就是充分利用计算机资源,使计算机的资源在每一时刻都能达到最大的利用率,不至于浪费计算机资源使其闲置。
3.线程的创建,停止,常用方法介绍。
  1.线程的创建:
  线程创建主要有2种方式,一种是继承Thread类,重写run方法即可;(Thread类实现了Runable接口)
  另一种则是实现Runable接口,也需要重写run方法。
  线程的启动,调用start()方法即可。 我们也可以直接使用线程对象的run方法,不过直接使用,run方法就只是一个普通的方法了。

  其他的还有: 通过匿名内部类的方法创建;实现Callable接口。。。。。

  2.线程常用方法:
  currentThread()方法:该方法返回当前线程的信息 .getName()可以返回线程名称。

  isAlive()方法:该方法判断当前线程是否处于活动状态。
  sleep()方法:该方法是让“当前正在执行的线程“休眠指定的时间,正在执行的线程是指this.currentThread()返回的线程。
  getId()方法:该方法是获取线程的唯一标识。
  3.线程的停止:
  在java中,停止线程并不简单,不想for。。break那样说停就停,需要一定的技巧。

  线程的停止有3种方法:
  1.线程正常终止,即run()方法运行结束正常停止。
  2.使用interrupt方法中断线程。
  3.使用stop方法暴力停止线程。
  interrupt方法中断线程介绍:
    interrupt方法其实并不是直接中断线程,只是给线程添加一个中断标志。
  判断线程是否是停止状态:
    this.interrupted(); 判断当前线程是否已经中断。(判断的是这个方法所在的代码对应的线程,而不是调用对象对应的线程)

    this.isInterrupted(); 判断线程是否已经中断。(谁调用,判断谁)
  
  注:.interrupted()与isInterrupted()的区别:
    interrupted()方法判断的是所在代码对应的线程是否中断,而后者判断的是调用对象对应的线程是否停止
    前者执行后有清除状态的功能(如连续调用两次时,第一次返回true,则第二次会返回false)
    后者没有清除状态的功能(两次返回都为true)
  真正停止线程的方法:
  异常法:
    在run方法中 使用 this.interrupted();判断线程终止状态,如果为true则 throw new interruptedException()然后捕获该异常即可停止线程。

  return停止线程:
    在run方法中 使用 this.interrupted();判断线程终止状态,如果为true则return停止线程。 (建议使用异常法停止线程,因为还可以在catch中使线程向上抛,让线程停止的事件得以传播)。
 
  暴力法:
    使用stop()方法强行停止线程(强烈不建议使用,会造成很多不可预估的后果,已经被标记为过时)
    (使用stop方法会抛出 java.lang.ThreadDeath 异常,并且stop方法会释放锁,很容易造成数据不一致)
  注:在休眠中停止线程:
    在sleep状态下停止线程 会报异常,并且会清除线程状态值为false;
    先停止后sleep,同样会报异常 sleep interrupted;

4.守护线程。
参考技术B 返回当前线程的名称:Thread.currentThread().getName()
线程的名称是由:Thread-编号定义的。编号从0开始。
线程要运行的代码都统一存放在了run方法中。
线程要运行必须要通过类中指定的方法开启。start方法。(启动后,就多了一条执行路径)
start方法:1)、启动了线程;2)、让jvm调用了run方法。
Thread类中run()和start()方法的区别:
start():用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
run():run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:start()方法最本质的功能是从CPU中申请另一个线程空间来执行 run()方法中的代码,它和当前的线程是两条线,在相对独立的线程空间运行,也就是说,如果你直接调用线程对象的run()方法,当然也会执行,但那是 在当前线程中执行,run()方法执行完成后继续执行下面的代码.而调用start()方法后,run()方法的代码会和当前线程并发(单CPU)或并行 (多CPU)执行。所以请记住一句话:调用线程对象的run方法不会产生一个新的线程,虽然可以达到相同的执行结果,但执行过程和执行效率不同
创建线程的第一种方式:继承Thread ,由子类复写run方法。
步骤:
1,定义类继承Thread类;
2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;
3,通过创建Thread类的子类对象,创建线程对象;
4,调用线程的start方法,开启线程,并执行run方法。
线程状态:
被创建:start()
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop()

创建线程的第二种方式:实现一个接口Runnable。
步骤:
1,定义类实现Runnable接口。
2,覆盖接口中的run方法(用于封装线程要运行的代码)。
3,通过Thread类创建线程对象;
4,将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。
为什么要传递呢?因为要让线程对象明确要运行的run方法所属的对象。
5,调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。
Ticket t = new Ticket();
/*
直接创建Ticket对象,并不是创建线程对象。
因为创建对象只能通过new Thread类,或者new Thread类的子类才可以。
所以最终想要创建线程。既然没有了Thread类的子类,就只能用Thread类。
*/
Thread t1 = new Thread(t); //创建线程。
/*
只要将t作为Thread类的构造函数的实际参数传入即可完成线程对象和t之间的关联
为什么要将t传给Thread类的构造函数呢?其实就是为了明确线程要运行的代码run方法。
*/
t1.start();
为什么要有Runnable接口的出现?
1:通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。
可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?
只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。
所以,通常创建线程都用第二种方式。
因为实现Runnable接口可以避免单继承的局限性。
2:其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。
所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。
实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象。

像这样的事情(Python多线程)的通常方法是啥?

【中文标题】像这样的事情(Python多线程)的通常方法是啥?【英文标题】:What is the usual approach for things like this (Python multithreading)?像这样的事情(Python多线程)的通常方法是什么? 【发布时间】:2022-01-16 05:52:46 【问题描述】:

我有以下代码:

def start_threads(self):
    thread_pool = []
    for obj in self._objs_list:
        thread = threading.Thread(target=self._do_task, args=(obj,))
        thread_pool.append(thread)
        thread.start()
    for thread in thread_pool:
        thread.join()

    self._do_final_task()

_do_task 基本上是一种处理一些 I/O 操作的方法,我想为 self._objs_list 中的每个 obj 并行执行它

我希望 self._do_final_task() 仅在所有线程完成工作后执行。

我的代码有效,但它非常丑陋且不成熟(看起来)。有没有更优雅的方法?

【问题讨论】:

如果您希望检查您的工作代码,请使用 Code Review Stack Exchange,而不是 ***。 哦,对不起。会的。 如果您想将问题重新表述为“此类事情的常用方法是什么?”,请继续。 @DavisHerring。完成,谢谢。 【参考方案1】:

虽然在不启动Thread 的情况下创建它可能很有用,但自动这样做的辅助函数通常是有意义的。该助手也可以是处理join上下文管理器

@contextlib.contextmanager
def spawn(*a,**kw):
  t=threading.Thread(*a,**kw)
  t.start()
  try: yield t
  finally: t.join()

class …:
  def start_threads(self):
    with contextlib.ExitStack() as es:
      for obj in self._objs_list:
        es.enter_context(spawn(target=self._do_task, args=(obj,)))
    self._do_final_task()

  …

【讨论】:

以上是关于多线程是啥的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程是啥意思?

java中啥叫做线程?啥叫多线程?多线程的特点是啥

java中啥叫做线程?啥叫多线程?多线程的特点是啥?

多线程和多进程分别是啥意思?

Python多线程是啥意思?

解决这个多线程问题的方法是啥?