我是靠谱客的博主 风趣大炮,最近开发中收集的这篇文章主要介绍深入理解Java进程与线程,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1.进程与线程

  1. 进程:程序由指令和数据组成,必须将指令加载到cpu,数据加载至内存。进程就是用来加载指令、管理内存、管理IO的。进程可以被视为程序的一个实例。
    当一个程序被运行,从磁盘加载这个程序的代码到内存,这时就开启了一个进程。
  2. 线程:一个进程之内可以分为一到多个线程。一个线程就是一个指令流,将指令流中的指令以一定的顺序交给cpu执行。
    在java中,线程作为最小调度单位,进程作为资源分配的最小单位。在windows中进程是不活动的,只是作为线程的容器。
  3. 进程与线程对比:
    1.进程基本上是相互独立的,而线程存在于进程内,是进程的一个子集。
    2.进程拥有共享的资源,如内存空间,供其内部的线程共享。
    3.进程间通信较为复杂:不同计算机之间的进程通信,需要通过网络,遵守共同的协议,例如HTTP。
    4.线程通信相对简单,因为他们共享进程内的内存,例如多个线程可以访问同一个共享变量。
    5.线程更轻量,线程上下文切换成本比进程低。
  4. 单核cpu下,线程实际还是串行执行的。一般会将这种线程轮流使用cpu的做法称为并发。
    多核cpu下,每个核都可以调度运行线程,这时候线程是可以并行的。
  5. 需要等待结果返回,才能继续运行就是同步;不需要等待结果返回,就能继续运行就是异步。
  6. 单核cpu下,多线程不能实际提高程序运行效率,只是为了能够在不同的任务之间切换。
    多核cpu可以并行跑多个线程,但能否提高程序运行效率还是要分情况的
    IO操作不占用cpu,只是一般拷贝文件使用的是阻塞IO,这时相当于线程虽然不用CPU,但需要一直等待IO结束,没能充分利用线程。

2.Java线程

  1. 创建和运行线程的方法、:
    1.直接使用Thread,例如将run()方法,即要执行的任务写在thread中
    2.视同Runnable配合thread,把线程和任务分开,thread代表线程,runnable代表可运行的任务
    PS:java8以后可以使用lambda精简代码:Runnable task = () -> log.debug(“hello”)
    原理之Thread与Runnable的关系
    (1)方法1是把线程和任务合并在了一起,方法2是把线程和任务分开了
    (2) 用Runnable更容易与线程池等高级API配合
    (3) 用Runnable让任务类脱离了Thread继承体系,更灵活
    3.FutureTask配合Thread
    FutureTask能够接收Callable类型的参数,用来处理有返回结果的情况
    FutureTask task3 = new FutureTask<>(() -> {
    log.debug(“hello”);
    return 100;
    });
    // 参数1 是任务对象; 参数2 是线程名字,推荐
    new Thread(task3, “t3”).start();
    // 主线程阻塞,同步等待 task 执行完毕的结果
    Integer result = task3.get();
    log.debug(“结果是:{}”,result);
  2. 查看进程线程的方法:
    1.windows:任务管理器可以查看进程和线程数,也可以用来杀死进程 tasklist查看进程 taskkill 杀死进程
    2.linux:ps -fe 查看所有进程 ps -fT -p 查看某个进程的所有线程 top按大写H切换是否显示线程
    top -H -p 查看某个进程的所有线程
    3.Java:jps查看所有Java进程 jstack查看某个进程的所有线程状态 jconsole查看某个进程中线程的运行情 况(图形界面)
  3. 原理之线程运行
    1.每个线程启动后,虚拟机就会为其分配一块栈内存。每个栈由多个栈帧组成,对应着每次方法调用时占用的内存;每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。
  4. 线程上下文切换:
    因为以下的一些原因导致cpu不再执行当前的线程,转而执行另一个线程的代码
    (1)线程的cpu时间片用完 (2)垃圾回收 (3) 有更高优先级的线程需要运行 (4)线程自己调用了sleep,yield,wait,join,park,synchronized,lock等方法。
    当线程上下文切换发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器,它的作用是记住下一条指令的执行地址,是线程私有的。
  5. 常见方法:
    (1) start() 启动一个新线程,在新的线程运行run方法里的代码start方法只是让线程进入就绪,里面代码不一定立刻运行(cpu的时间片还没分给它)。每个线程对象的该方法只能调用一次否则会抛出illegalThreadStateException
    (2)run() 新线程启动后会调用的方法,如果构造Thread对象时传递了Runnable参数,启动后会调用参数中的run方法,否则默认不执行任何操作。但可以创建Thread的子类对象,来覆盖默认行为。
    (3)join() 等待线程运行结束
    (4) getState() 获取线程状态,Java中用6个enum枚举类表示,分别为NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED
    (5) interrupt() 打断线程,如果被打断线程正在sleep,wait,join会导致被打断的线程抛出InterruptedException,并清除打断标记;如果打断的是正在运行的线程,则会设置打断标记;park的线程被打断,也会设置打断标记,不会清空打断状态;如果打断标记已经是true,则park会失效
    (6) isInterrupted 判断是否被打断 不会清除打断标记
    (7) interrupted 判断当前线程是否被打断,会清除打断标记,源码实际上就是调用isInterrupted(true)
    (8) yield() 提示线程调度器让出当前线程对cpu的使用 主要是为了测试
  6. start与run
    1.直接调用run是在主线程中执行了run,没有启动新的线程
    2.使用start是启动新的线程,通过新的线程间接执行run中的代码
  7. sleep与yield
    1.sleep:(1)调用sleep会让当前线程从Running进入Timed Waiting状态(阻塞)
    (2) 其他线程可以使用interrupt方法打断正在睡眠的线程,这时会抛出InterruptedException
    (3) 睡眠结束后的线程未必会立刻得到执行
    (4) 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
    2.yield: (1)调用yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其他线程
    (2)具体的实现依赖操作系统的任务调度器(如果调用yield之后没有其他线程被调度,则仍会运行该线程)
    3.线程优先级:线程优先级会提示调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
    如果cpu比较忙,那么优先级高的线程会获得更多的时间片,但cpu闲时,优先级几乎没作用
  8. 主线程与守护线程
    1.默认清空下,Java进程需要等待所有线程都运行结束才会结束。但有一种特殊的线程叫守护线程,只要其他非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。
    2.垃圾回收器线程就是一种守护线程
    Tomcat中的Acceptor和Poller线程都是守护线程,所以接收到shutdown命令后,不会等待他们完成请求
  9. 五种状态:这是从操作系统层面来描述的
    1.初始状态:仅是从语言层面创建了线程对象,还未与操作系统线程关联
    2.可运行状态:(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由cpu调度执行
    3.运行状态:获取了cpu时间片运行中的状态,当cpu时间片用完,会从运行状态转换至可运行状态,上下文切换
    4.阻塞状态:如果调用了阻塞API,如BIO读写文件,这时该线程实际不会用到cpu,会导致线程上下文切换,进入阻塞状态。 等BIO操作完毕,会由操作系统唤醒阻塞的线程,转换至可运行状态
    与可运行状态的区别是,对阻塞状态的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们
    5.终止状态表示线程已经执行完毕,生命周期已经结束,不会再转换为其他状态。
  10. 六种状态:这是从Java API层面来描述的,根据Thread.State枚举,分为六种状态
    1.NEW线程刚被创建,但是还没有调用start方法
    2.RUNNABLE为调用了start()之后,PS:JAVA API层面的RUNNABLE状态涵盖了操作系统层面的可运行状态、运行状态、阻塞状态(由于BIO导致的线程阻塞,在JAVA中无法区分,仍然认为是可运行)
    3.BLOCKED、WAITING、TIMED_WAITING都是JAVA API层面对阻塞状态的细分
    4.TERMINATED代表线程代码运行结束

最后

以上就是风趣大炮为你收集整理的深入理解Java进程与线程的全部内容,希望文章能够帮你解决深入理解Java进程与线程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(59)

评论列表共有 0 条评论

立即
投稿
返回
顶部