线程创建总结

本文脉络
进程和线程

什么是进程
进程是系统进行资源分配的基本单位。
某个程序在运行时,操作系统需要将一些系统资源分配给其使用,比如内存,CPU等。操作系统按照进程的维度来进行资源的分配。
程序不等于进程。一个程序可能开启多个进程。如登录了多个QQ账户等。
什么是线程
线程是系统进行资源调度的基本单位。
一个进程中通常包含多个线程。每个线程即是一条程序执行的具体线路。多个线程中,必须包含一个主线程。进程启动时,会从主线程开始执行,主线程调起其它子线程。子线程也可以被另一个子线程调起。主线程结束意味着进程结束,其它子线程也会结束,即使任务未完成。
线程与CPU
在这里插入图片描述

CPU中通常包含多个核,每个核主要由三部分组成,即控制器,运算器和寄存器。寄存器用于存放指令和数据,运算器用于执行指令,控制器则对运算器和寄存器进行调度。
CPU的线程切换是如下进行的:

  1. 控制器将A线程的指令及数据装入寄存器,运算器执行A线程相关的计算。
  2. 当要切换至B线程时,控制器将寄存器中原A线程的数据放入缓存,重新装入B线程的指令及数据,运算器执行B线程。
    在上述过程中,运算器仅仅只是针对寄存器中的指令或数据进行运算,它并不关心这些数据来源于哪个线程。线程的控制主要基于控制器。
    单位时间里,一个核只能运行一个线程。当要运行的线程数,超过CPU核心数时,就会发生线程的切换或等待。
    线程并不总是在使用CPU。线程的底层操作只有两种,一种是计算,即需要使用CPU。另一种是IO,即读写,无论是从磁盘或者网络,都不需要CPU。
    多少线程合适
    线程过少,会造成任务堆积,无法充分利用CPU。
    线程过多,会造成线程的频繁切换,也会造成不必要的资源浪费。
    在分配资源时,还要考虑保留一部分CPU资源以应对突发状态。则可参考如下公式:
    线程数 = 核数 * 百分比 * (1 + W/C)
    其中,W为线程的等待时间,即执行IO的时间。C为线程的计算时间,即需要使用CPU的时间。W和C均为多个线程的一个平均时间。
    此公式仅作为一个参考,用于设置初始值。后续,需要通过不断的观察或者以往的经验来进行不断的修正,以达到最佳的状态。
    线程概述

线程创建的几种方式
继承Thread类。
实现Runnable接口。
实现Callable接口。
创建ThreadPool。从线程池中获取。
常用的方式是ThreadPool,也推荐使用此方式。ThreadPool通过池的方式来管理线程,避免了频繁创建和销毁线程带来的开销,也控制了线程的最大数量,减少系统资源被耗尽的风险。
就其它三种方式来说,Callable接口形式可以提供一个返回值,通过 Future 可以取得该返回值。Runnable接口优于Thread继承,因为Java是单继承的,但是可以实现多个接口。
Java8以后,可以通过Lambda方式来创建线程。这种方式只是一个语法上的便利,本质仍然是接口的实现。
线程的状态
线程的状态

线程在其整个生命周期中,存在几种状态,如新建,就绪,运行,等待,阻塞,销毁等。就绪和运行又可合称为可执行。
处于就绪状态的线程已经具备可运行的条件,在队列中等待CPU的调度。
线程调用wait方法后进入等待状态,需要其它线程唤醒或经过预设的时间后自动唤醒。
线程在竞争某资源时,资源被其它线程占用而无法取得,则进入阻塞状态。
从CPU的角度来说,就绪,等待和阻塞的本质是三个线程队列,CPU通过合适的策略来调用队列中的线程。

多线程初步相关知识总结。
①线程是什么?
线程是程序运行的一个执行路径,是程序执行的最小运行单元,与它相关的,进程是一个正在运行的应用程序,比如exe,服务等。
一个进程至少包含一个线程,一般是多个线程并行执行以提高程序运行效率,在单核处理器中有时间片的概念,线程其实是非常快速的拿到时间片交替执行,看起来像是并行一样,实际上在单核处理器根本没有真正的并行。
我们在Java程序中合理使用多线程来提高程序的执行效率,非必要一般不会使用。
②创建线程的几种方式,
1:自定义一个线程类继承thread类,重写父类的run方法作为线程的执行逻辑,在调用类中我们创建自定义线程类的对象new thread自定义类,此时线程处于new新建状态,线程没有启动。使用对象.start方法使线程进入可以运行状态,此阶段称为runnable。
我们引申说一下包括以上两种状态的线程生命周期:
第一个:new 新建状态,
二:runnable可以运行状态,调用start方法后处于此状态。
三:blocked:锁阻塞状态,没有获取到锁就会处于这个状态,一直等待直到拿到锁进入资源操作,就进入runnable运行状态,
四:waiting无限等待状态,runnable运行中线程使用了wait方法处于这个状态,如果被其他线程notify但未获得锁对象进入blocked,被其他线程notify并获得锁对象进入runnable状态。
五:timed_waiting计时等待状态,线程执行时被调用sleep方法或者wait方法毫秒级处于此状态。sleep时间到或者wait时间到并获得锁对象进入runnable,wait时间没到被其他线程notify唤醒并获得锁对象进入runnable。wait时间到没获得锁或者wait时间没到被其他线程唤醒,但是未获得锁对象进入blocked阻塞状态直到获取锁为止。
六:terminated终止状态,线程执行完毕或者遇到异常时,处于这个状态。
2、我们继续说创建线程的第二种方式,自定义线程类实现runnable接口,重写run方法,调用类中new thread对象传入参数为实现runnable类接口对象,这里可以使用匿名内部类以及lambda表达式简化,直接写run方法线程的运行逻辑。
线程本身是执行run中任务的一个运行实例,使用线程是为了能使run中的代码与其他线程并行执行以提高效率。
3、创建线程第三种方式,实现callable接口,自定义类实现callable尖括号泛型里面写返回值对象类型,重写call方法,可以指定返回值类型,调用类中callable泛型 对象new 自定义类,第二步使用futuretask泛型中返回值类型,new futuretask构造器参数传入上面的callable对象,第三步new thread构造器参数传入futuretask对象,此时一个新线程算是创建成功了,我们使用线程对象点start方法启动这个线程。后面可以使用futuretask对象get方法获取上面定义的返回值类型的结果,这个方法会等待上面的线程执行完毕再获取结果。
这三种创建线程的方式,继承thread类有单继承局限性,优点是编写简单,runnable接口可以在线程代码量少时直接匿名内部类开启,使用callable接口方式弥补了以上两种没有返回值的缺陷,可以获得结果,缺点是编写相对复杂。
线程中的锁——初步总结,
使用场景:多线程并行执行引发并发问题,当共享资源被多个线程争抢执行时,一个线程运行到共享代码的其中一行由于业务时间问题还未操作数据成功,另一个线程此时可能就已经进入并修改了数据,这导致了多线程操作数据不一致的安全问题。此时我们需要使用锁来同步资源,目的是让多线程情况下,共享资源在同一时间只有一个线程操作修改数据,修改完毕其他线程才能抢到锁并进入代码运行。目前两种方式,synchronized同步关键字,可以加在类上,方法上,代码块上,加在代码块上用大括号把需要同步的代码块括住关键字后小括号中传入this当前线程对象。
第二种方式,使用lock类锁对象,在线程中创建privatefinal字段reetrantlock,lock点lock加锁,中间是锁住代码,unlock解锁

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值