【多线程学习一】进程与线程

多线程

多线程可以充分利用多核的性质去提高效率

线程的栈内存相互独立,每个线程有自己独立的栈内存,有多个栈帧,互不干扰

进程与线程的联系

  • 一个进程至少要有一个线程(主线程)
  • 线程之间可以进行内存资源(进程提供)共享,可以进行通信
  • 每个线程都有自己的运行回路

怎么创建一个线程

  • 继承Thread类(无返回值)


    在这里插入图片描述

    • Thread其实也是一个Runnable的实例,通过实例化类后调用start()方法执行线程
  • 实现Runnable接口(无返回值)

    在这里插入图片描述
    在这里插入图片描述

    • 可使用lambda表达式

      Runnable r = ()-> System.out.println("running");
      Thread thread = new Thread(r);
      thread.start();
      
    • 必须实现Runnable接口的run()方法,启动线程时先将线程实例化后作为参数传给Thread类,由Thread执行线程,此时会判断目标参数是否有run方法,有则实现目标参数的run,无则使用thread的run方法

  • 使用Callable和Future

    • 当有返回值时,必须实现Callable接口,返回Future对象,通过线程池ExecutorService返回Future对象

    -在这里插入图片描述
    在这里插入图片描述

更推荐使用Runnable接口,可以与线程池等高级API配合

​ 摆脱了Thread继承体系,更灵活(组合优于继承–》组合重用原则)

线程的状态

按照操作系统分5种

  • new 新建状态,当创建一个线程后,没有执行start()方法,所以还处于new新建状态,此时由JVM分配内存,并初始化其变量

  • runnable 就绪状态,执行start()方法后,只是一个就绪状态,能不能运行需要看CPU调度,JVM调用栈和程序计数器,等待调度运行

  • running 运行状态,当就绪的线程分配到CPU,开始执行run()方法,

  • block 阻塞状态,线程因某些原因让出了CPU使用权,让出了CPU时间片,进入等待,直到线程进入就绪状态才有机会获得时间片,转为运行状态

    • 等待阻塞

      运行状态的线程执行了wait()方法,JVM把该线程放入等待队列

    • 同步阻塞

      运行状态的线程A获取对象的同步锁(如synchronized),而该同步锁被线程B占用时,JVM把线程A放到锁池

    • 其他阻塞

      运行状态的线程执行了sleep()、join()方法时,或发出IO请求,JVM会把该线程置为阻塞状态

      当sleep()状态超时,join()等待线程终止或超时时,IO请求处理完毕时,线程会重新转入就绪状态

  • dead 死亡状态,三种方式

    • 线程run()或call()方法执行完成,线程正常结束
    • 线程抛出未捕获的异常,线程异常结束
    • 线程==调用stop()==方法,但是该方法容易导致死锁,不推荐使用
      在这里插入图片描述[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eBMWUMgl-1655217522850)(D:\Note\面试学习笔记.assets\image-20220614105344477.png)]

按Java API层面划分六种

BLOCKED , WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分
在这里插入图片描述

start() & run()

start()方法执行后线程是为就绪状态,是Thread启动线程的唯一方法,启动线程后无需等待run()方法体执行完毕,可以直接继续执行下面的代码。start()是一个native方法

start()启动后自动调用run()方法,转为运行状态,run()方法运行结束后,线程终止。CPU调度其他线程

start调用run–》异步执行,直接调用run–》同步执行

每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException

sleep睡眠状态

调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)

写在哪个线程中就让哪个线程睡眠

其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException

睡眠结束后的线程未必会立刻得到执行

建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

sleep适用于无需锁同步的场景

yield

调用 yield 会让当前线程Running 进入 Runnable 就绪状态,然后调度执行其它线程

具体的实现依赖于操作系统的任务调度器

线程优先级

在这里插入图片描述

  • 会提示调度器优先调度该线程,但是仅仅是一个提示,调度器可以忽略它
  • cpu空闲时,设置优先级几乎没作用

yield和优先级不是很容易去控制线程的调度

为了不让while(true)空转浪费cpu,可以使用sleep或yield让出cpu的使用权给其他程序

join

等待线程运行结束

异步
在这里插入图片描述

static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
    test2();
}
private static void test2() throws InterruptedException {
    Thread t1 = new Thread(() -> {
        sleep(1);
        r1 = 10;
    });
    Thread t2 = new Thread(() -> {
        sleep(2);
        r2 = 20;
    });
    long start = System.currentTimeMillis();
    t1.start();
    t2.start();
    t1.join();  //等待t1
    t2.join();	//等待t2
    long end = System.currentTimeMillis();
    log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}

将指定的线程t1加入线程,让t2进入等待,直到t1的生命周期结束,此期间t2处于阻塞状态

interrupt打断阻塞

打断 sleepwaitjoin 的线程这几个方法都会让线程进入阻塞状态

打断 sleep 的线程, 会清空打断状态,以 sleep 为例

打断正在运行的线程

在这里插入图片描述

  • isInterrupted()判断是否打断,不会清除标记
  • interrupter() 判断是否打断,会清楚标记

不推荐

容易破坏代码块,造成线程死锁,且方法已过时

  • stop
  • resume
  • suspend

以上用wait去替换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值