【多线程笔记】基础

概念

信号

有时候你需要让线程处于等待状态,直到接收其他线程发来的消息,这就叫发送信号(signaling)
最简单的发送信号的方式就是使用ManualResetEvent。调用它的WaitOne()方法阻塞线程,调用Set()方法开启信号

临界区

一段代码内如果存在对共享资源的多线程读写操作,那么称这段代码为临界区。如:

static int counter = 0;
static void increment()
{// 临界区
 counter++;
}
static void decrement()
{// 临界区
 counter--;
}

竞争条件

当两个或两个以上的线程访问共享数据,并且尝试同时改变它时,就发生争用的情况。它们所依赖的那部分共享数据,叫做竞争条件。
数据争用是竞争条件中的一种,出现竞争条件可能会导致内存(数据)损坏或者出现不确定性的行为。

线程同步

如果有 N 个线程都会执行某个操作,当一个线程正在执行这个操作时,其它线程都必须依次等待,这就是线程同步。
多线程环境下出现竞争条件,通常是没有执行正确的同步而导致的。

阻塞

阻塞状态指线程处于等待状态。当线程处于阻塞状态时,会尽可能少占用 CPU 时间。
当线程从运行状态(Runing)变为阻塞状态时(WaitSleepJoin),操作系统就会将此线程占用的 CPU 时间片分配给别的线程。当线程恢复运行状态时(Runing),操作系统会重新分配 CPU 时间片。
分配 CPU 时间片时,会出现上下文切换。

内核模式、用户模式?

只有操作系统才能切换线程、挂起线程,因此阻塞线程是由操作系统处理的,这种方式被称为内核模式(kernel-mode)。
Sleep()Join() 等,都是使用内核模式来阻塞线程,实现线程同步(等待)。
内核模式实现线程等待时,出现上下文切换。这适合等待时间比较长的操作,这样会减少大量的 CPU 时间损耗。
如果线程只需要等待非常微小的时间,阻塞线程带来的上下文切换代价会比较大,这时我们可以使用自旋,来实现线程同步,这一方法称为用户模式(user-mode)。

对象锁

它采用互斥的方式让同一时刻至多只有一个线程持有对象锁,其他线程如果想获取这个锁就会阻塞住,这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换

线程的五个状态

新增状态:线程刚创建Thread t = new Thread();
就绪状态:线程执行start方法、sleep时间到、io方法返回、获得同步锁、收到通知
运行状态:当就绪状态的线程获得CPU资源
阻塞状态:当线程执行sleep、wait方法、IO阻塞、等待同步锁、等待通知
死亡状态:执行完成或异常

线程、栈、栈帧

线程启动,虚拟机为其分配一块栈内存
每个栈由多个栈帧(Frame)组成
每个栈帧对应每次方法调用占用的内存
每个线程只有一个活动栈帧,对应当前正在执行的方法

程序运行

线程上下文切换(Thread Context Switch)

因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码

  • 线程的 cpu 时间片用完(每个线程轮流执行,看前面并行的概念)
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念 就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的

posted @ 2019-12-08 16:41  .Neterr  阅读(1202)  评论(0编辑  收藏  举报