了解多线程的意义和作用

每日一诗

忆江上吴处士

——贾岛

闽国扬帆去,蟾蜍亏复圆。
秋风生渭水,落叶满长安。
此地聚会夕,当时雷雨寒。
兰桡殊未返,消息海云端。

一、进程与线程

进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。

线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

二、影响服务器吞吐量的因素

  • 硬件:CPU、内存、磁盘、网络
  • 软件:最大化的利用硬件资源
    线程数量、JVM内存分配大小、网络通信机制(BIO、NIO、AIO)、磁盘IO
  • 线程数量提升服务端的并发数量

三、并法与并行

并行是指两个或者多个事件在同一时刻发生;

并发是指两个或多个事件在同一时间间隔内发生,这个词可以冲宏观和微观两个层面来讲,如果从微观
角度来看。以线程为例,假设当前电脑的cpu是单核,但是能不能支持多线程呢?当然也是能的,此时
如果是多线程运行的话,那么CPU是通过不断分配时间片的方式来实现线程切换,由于切换的速度足够
快,我们很难感知到卡顿的过程。

四、java中的线程

4.1线程的三种方式

  1. Runnable接口
  2. Tread类
  3. Callable/Future 带返回值的

4.2 Thread这个工具在哪些场景可以应用

  • 网络请求分发的场景
  • 文件导入
  • 短信发送场景

四、线程的基础

4.1 java线程状态(6种)与操作系统线程状态(5种)

4.1.1 Java 线程中几个状态说明:

定义在Thread类中的 State枚举中,可以直接查看代码中的注释

java.lang.Thread. State . NEW | RUNNABLE | BLOCKED | WAITING | TIMED_WAITING | TERMINATED

状态解释说明
NEW新建一个线程对象,还没有调用start()方法new一个线程实例,线程还没有start
RUNNABLEJava线程中将就绪(READY)和运行中(RUNNING)两种状态统一为 RUNABLE等待被调度或正在运行中
BLOCKED表示线程阻塞于锁线程阻塞在进入synchronized修饰的方法或代码块时的状态
WAITING进行该状态的线程需要等待其他线程做出一些特定动作(通知或中断)让出CPU,等待被显示的唤醒,被唤醒后进入BLOCKED状态,重新获取锁
TIMED_WAITING不同于waiting,它可以在指定的时间后自行返回超时等待,让出CPU,时间到了自动唤醒,进入BLOCKED状态,重新获取锁
TERMINATED表示显示已经执行完毕
  • RUNNABLE

    Java中 RUNNABLE 对应操作系统的几个状态

  • READY

    • 有资格运行,但还没有被调度
    • 调用线程的 start()方法,进入就绪状态
    • 当前线程 sleep() 方法结束,其他线程 join() 结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态
    • 当前线程时间片用完了,调用当前线程的 yield()方法,当前线程进入就绪状态
    • 锁池里的线程拿到对象锁后,进入就绪状态
  • RUNNING

    线程调度程序选中该线程进行运行

  • 操作系统线程阻塞等待 I/O

    JVM 并不关心操作系统线程的实际状态,从 JVM 看来,等待CPU使用权(操作系统状态为可运行态)与等待 I/O(操作系统处于等待状态)没有区别,都是在等待某种资源,所以都归入RUNNABLE 状态

  • TERMINATED

    线程 run()方法完成时,或者主线程main()方法结束时,就认为它终止了。

    这个线程对象也许是活的,但是已经不是一个单独执行的线程了,线程一旦终止了就不能复生,在一个终止的线程上调用 start()方法,会抛出 java.lang.IllegalThreadStateException 异常

    BLOCKED

    由synchronized锁导致进入该状态(WAITING,TIMED_WAITING状态下唤醒也可能进入该状态)

    (Java中的 BLOCKED状态与操作系统中的 阻塞状态不同)

    WAITING

    各情况下进入该状态,线程不会占用CPU,线程会让出锁,等待被其他线程唤醒,然后会进入 BLOCKED 状态,重新竞争锁。

    TIMED_WAITING

    各个情况下进入该状态,线程不会占用CPU,时间到了自动唤醒

  • 如果是因为sleep进入该状态,线程不会释放锁,等到时间到了自动唤醒进入RUNNABLE状态

  • 其他情况下线程会释放锁,等待其他线程唤醒,超时时间到了自动唤醒,然后进入 BLOCKED状态,重新竞争锁

 4.1.2  java中的线程与操作系统中线程的区别

操作系统中进程(线程)的状态有:

  • 初始状态(NEW)

    对应 Java中的

  • 可运行状态(READY)

    对应 Java中的 RUNNBALE 状态

  • 运行状态(RUNNING)

    对应 Java中的 RUNNBALE 状态

  • 等待状态(WAITING)

    该状态在 Java中被划分为了 BLOCKEDWAITINGTIMED_WAITING 三种状态

    当线程调用阻塞式 API时,进程(线程)进入等待状态,这里指的是操作系统层面的。从 JVM层面来说,Java线程仍然处于 RUNNABLE 状态

    JVM 并不关心操作系统线程的实际状态,从 JVM 看来,等待CPU使用权(操作系统状态为可运行态)与等待 I/O(操作系统处于等待状态)没有区别,都是在等待某种资源,所以都归入RUNNABLE 状态。

  • 终止状态 (TERMINATED)

4.2 Java 的线程运行周期图

上图还有些欠缺,例如:

  1. WAITING和TIMED_WATING,在唤醒后,如果有锁,是有可能进入到阻塞状态等待锁的
  2. 缺少了一个阻塞状态获取锁后变成就绪状态
  3. 线程由运行到终止态,除了线程运行结束,还应加上stop()、interrupt()

 

需要注意的是,操作系统中的线程除去 new 和 terminated 状态,一个线程真实存在的状态,只有:

  • ready :表示线程已经被创建,正在等待系统调度分配CPU使用权。
  • running :表示线程获得了CPU使用权,正在进行运算
  • waiting :表示线程等待(或者说挂起),让出CPU资源给其他线程使用

在加上新建状态和死亡状态,一共5种。

疑问: LockSupport.unpark()是干啥的?
——后续再研究

4.3线程的启动的时候做了什么

  1.  调用start()方法,实际上java会调用 native start0()方法。
  2. start0()方法会调用JVM里的c++方法,后续会通过create_thread方法和start_thread方法
  3. create_thread方法会在系统中创建一个线程
    start_thread会在系统中启动线程,在线程调度的时候,会调用系统中线程的调用算法,分配给不同的CPU
  4. 而后,系统会调用jvm线程的run方法,在调用对应java层面的run方法
  5. 在线程调用结束后,jvm会做销毁动作

4.4线程的终止

4.4.1 stop()——在java多线程中已经废弃

  1. stop()方法会导致释放锁的不良后果,数据不完整。
           比如一个上锁了得方法:threadA线程拥有了监视器,这些监视器负责保护某些临界资源,比如说银行的转账的金额。当正在转账过程中,main线程调用 threadA.stop()方法或者this.stop()。结果导致监视器被释放,其保护的资源(转账金额)很可能出现不一致性。比如,A账户减少了100,而B账户却没有增加100,没有保证数据原子性
  2. 当线程调用stop()方法时,会立刻终止线程的所有操作,并抛出ThreadDeath异常,通常不需要捕捉

4.4.2 interrupt()

  • 设置一个共享变量的值 true
  • 唤醒处于阻塞状态下的线程

我的理解:
interrupt()方法在线程不阻塞的时候不会中断线程,在线程阻塞的时候,会使线程抛出interruptException异常来达到中断线程的效果。

 相关方法:

方法描述
interrupt()中断线程,将会设置该线程的中断状态位,即设置为true
isInterrupted()判断某个线程是否已被发送过中断请求
interrupted()判断某个线程是否已被发送过中断请求,并且将中断状态重新置为false(有一说一,这个方法名取的真离谱)

interrupt简述

interrupt() 方法只是改变中断状态而已,它不会中断一个正在运行的线程。这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程就得以退出阻塞的状态。 更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。

  1. 线程处于阻塞状态: 当线程使用了sleep,wait,socket中的receiver,accept等方法时。
    此时调用线程的interrupt方法,会抛出interruptException。 抛出异常时,程序被阻塞中断标志复位,变为false,也就是调用isInterrupted()会返回false。
    阻塞中的方法抛出异常,通过代码捕获,然后break跳出循环状态,才能正常结束run方法。
  2. 线程是未阻塞状态, 使用isinterrupt()方法判断线程的中断标志来退出循环。
    使用interrupt方法,会把中断标志设置为true。
    和使用自定义标志来控制循环是一样的。  

 注意:调用sleep,wait等方法时,代码提示会抛出interruptException。

t1.isInterrupted():返回当前程序中断标志位是否中断。——实例方法

t1.interrupt():中断。——实例方法

Thread.interrupted():复位。——静态方法

demo如下:

public class InterruptDemo02 implements Runnable{
    @Override
    public void run() {
        while(!Thread.currentThread().isInterrupted()){ //false
            try {
                TimeUnit.SECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
                //可以不做处理,
                //继续中断 ->
                Thread.currentThread().interrupt(); //再次中断
                //抛出异常。。
            }
        }
        System.out.println("processor End");
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(new InterruptDemo02());
        t1.start();
        Thread.sleep(1000);
        t1.interrupt(); //有作用 true
        Thread.interrupted() ;//复位
    }
}

五、课后作业

5.1 简述线程生命周期和触发机制

线程的生命周期和触发的机制

5.2  思考在现有的项目中,用图形描述哪些场景可以改造成异步化

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值