Java 并发(一)—— 线程

一、基本概念

1.并发和并行

  • 并发:多个程序、交替执行
  • 并行:多个程序、同时执行

2.CPU、进程、线程

  • 一个操作系统有多个CPU;一个CPU有多个进程;一个进程有多个线程。
  • 各个CPU之间共享操作系统资源;各个进程之间共享CPU资源;各个线程之间共享进程资源。
  • 所谓操作系统 / CPU的任务调度,实际上的调度对象是线程进程只是给线程提供了资源

CPU:并发运行多个任务(指 进程、线程、中断)。CPU 时间被划分为一段段的时间片,这些时间片再被轮流分配给各个任务。

CPU上下文 = CPU寄存器 + 程序计数器

【CPU上下文 具体分为 进程上下文 / 线程上下文 / 中断上下文

进程:资源分配的单位。包括内核和用户空间的资源。由内核管理和切换。

线程:CPU调度的单位。包括独立的寄存器和栈。


二、线程的实现

  • 用户线程:由用户空间程序管理和调度的线程,运行在用户空间(专门给应用程序使用)。
  • 程序一般不会直接去使用内核线程,而是使用内核线程的一种高级接口——轻量级进程
  • 内核线程:由操作系统内核管理和调度的线程,运行在内核空间(只有内核程序可以访问)。

用户线程创建和切换成本低,但不可以利用多核CPU。

内核态线程,创建和切换成本高,可以利用多核CPU(每个内核线程都是独立的调度单元。一个被阻塞,不影响其他)。

三、线程状态及转换(Java)

线程的状态,指的是Thread 类中threadStatus的值。该值映射后对应上表 各个线程状态。 

private volatile int threadStatus = 0; 

//threadStatus值 映射的枚举类
public enum State {  
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

1.new、runnable、running、terminatated

  • Thread t = new Thread() 创建对象,这个时候,线程t就处于NEW状态。但他还不是“线程”。
  • t.start()后,会执行一个native方法创建内核线程。这时候才有一个真正的线程创建出来,并即刻开始运行。这个内核线程与线程t进行1:1的映射。这时候t具备运行能力,进入RUNNABLE状态
  • 处于RUNNABLE且未运行的线程,会进入一个就绪队列中,等待操作系统的调度。等线程t获得了 CPU 时间片后就处于RUNNING状态
  • 当一个线程执行完毕(或者调用已经不建议的 stop 方法),线程的状态就变为 TERMINATED。进入TERMINATED后,线程的状态不可逆,无法再复活。

引申:可以直接调用Thread类的run方法吗?

调用 start() 方法方可启动线程并使线程进入就绪状态,直接执行 run() 方法的话不会以多线程的方式执行。

new 一个 Thread,线程进入了新建状态。调用 start()方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 但是,直接执行 run() 方法,会把 run() 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。

2.blocked、waiting

  1. 触发条件不同
  2. 唤醒条件不同
  3. 中断响应不同

阻塞  获取一个对象的锁(monitor lock),但该锁已经被另一个线程持有。

         由JVM调度器来决定唤醒自己,而不需要由另一个线程来显式唤醒自己

         不响应中断

        (非java.util.concurrent库中的锁,即synchronized)

等待  等待另一个线程执行某些操作。这种状态下,线程将不会消耗CPU资源,不会参与锁的竞争

         需要等待另一个线程显式地唤醒自己

         可响应中断

        (Object.wait()、Thread.join()以及等待Lock或Condition)

3.状态转换

sleep() 和 yield() 类似,都会让当前线程交出CPU权限,让CPU去执行其他的线程。也都不会释放锁,只是回到的状态不同。

join()实际是利用了wait(),只不过它不用等待notify()/notifyAll(),且不受其影响。它结束的条件是:1)等待时间到;2)目标线程已经run完(通过isAlive()来判断)。


四、创建线程的方式

1.继承Thread类

2.实现Runnable接口

3.实现Callable接口与FutureTask

4.使用线程池(Executor框架)

引申:

严格来说,Java就只有一种方式可以创建线程,那就是通过new Thread().start()创建。

而所谓的Runnable、Callable……对象,这仅仅只是线程体,也就是提供给线程执行的任务,并不属于真正的Java线程,它们的执行,最终还是需要依赖于new Thread()。


五、参考

Java并发编程面试题 | 小林coding (xiaolincoding.com)

Thread详解 - waterystone - 博客园 (cnblogs.com)

Java锁与线程的那些事 (youzan.com)

Java并发常见面试题总结(上) | JavaGuide

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值