线程和进程

目标:

1.什么是进程?什么是线程?

2.线程的生命周期?

3.怎么创建线程?

一、什么是进程和线程

1.1 应用程序

应用程序是指为完成某项或多项特定工作的计算机程序,它运行在用户模式,可以和用户进行交互,具有可视的用户界面。应用程序通常又被分为两部分:图形用户接口(GUI)和引擎。

1.2 进程

进程是操作系统进行资源分配的最小单位。

什么是资源?

CPU、内存空间、磁盘IO都是资源

1.3 什么是线程?

线程是CPU调度的最小单位。线程不能独立存在,必须依赖于进程。

1.4 CPU核心数和线程数的关系?

1.4.1 多核

一个CPU有多个核心。

内核处理器

一个CPU内核可以执行一个线程。

逻辑处理器

同时可以处理8个线程。

1.5 CPU时间片轮转技术

把CPU时间切片,(RR调度),分配给多个线程使用。

CPU 时间片轮转机制是一种抢占式调度算法,即 CPU 会分配给每个进程一个固定时间片,当一个进程的时间片用尽后,系统会打断该进程并分配给下一个进程。这一过程会一直进行下去,直到所有进程都被执行完毕。

实现原理:

1)系统将所有的就绪进程按先来先服务的原则,排成一个队列,

2)每次调度时,把CPU分配给队首进程,并令其执行一个时间片.时间片的大小从几ms到几百ms.

3)当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;

4)然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片

1.5.1 上下文切换

上下文切换是非常耗费CPU时间的一个操作。一次上下文切换需要耗费20000个CPU周期。

二、CPU调度

CPU调度,涉及并行和并发的概念。

2.1 并行和并发

并行:同时执行不同的任务;

并发:交替执行不同的任务。

两者区别

1、并发:并发在一台处理器上“同时”处理多个任务。
2、并行:并行在多台处理器上同时处理多个任务。

2.2 为什么要使用多线程和高并发?

1)充分利用CPU资源

2)加快用户响应时间

2.2.2 高并发带来的问题

线程并非越多越好?

操作系统限制线程数:Linux: 1000个;Windows:2000个。

为什么需要限制线程数:线程分配需要占据资源(栈空间);

实际开发中,多线程可以使用线程池。

Java天生就是多线程的。

三、创建线程的方式

3.1 Thread类启动

new Thread().start()

3.2 通过Runnable接口启动

public class MyRunnable implement Runnable

3.3 Thread类和Runnable接口区别?

Java对线程的抽象Thread;

Runnable是对业务逻辑的抽象。

3.4 Callable接口

Callable实现Runnable接口,本质上与Runnable是同一种实现。

四、Thread操作

4.1 start

启动线程。new Thread只是创建一个线程对象。只有start以后,才开始真正启动线程。

如果创建线程以后,调用两次start方法,会怎么样?

抛出异常。因为启动线程的时候会判断线程状态。

4.1.1 start与run方法的区别?

start启动线程,与操作系统挂钩。run是用户业务逻辑的处理。

4.2 join方法

A线程中,B线程join,待B执行完成以后,再接着执行A线程。

线程的执行变为串行。

join阻塞当前线程,执行新的线程任务,新任务执行完成后,在继续执行此任务

4.4 停止线程stop

stop方法过期。stop强制把当前线程干掉,不管当前线程是否安全释放资源。

导致线程占用的资源不会正常的释放。

4.4.2 线程中断interrupt

结束线程可以使用interrupt中断。

  • interrupt:对线程进行中断。实际上是给线程设置一个中断标志位。
  • inpterrupted: 也可以判断当前线程是否被中断。
  • isInterrupted: 判断当前线程是否被中断

JDK线程是协作式的,而不是抢占式的。interrupt以后,线程也可以完全不处理中断标志位。只是通知线程中断。

inpterrupted是一个静态方法,设置当前线程中断标志位,线程进入中断以后会将中断标志位重置。

尽量使用interrupt来结束线程

4.4.3 Runnable中怎么判断线程是否需要中断?

Thread.currentThread.isInterrupted() 来判断

线程处于死锁状态是不会理会中断的。

sleep/wait中断时,外部线程发起interrupt操作,当前线程会抛出 InterruptException,但是不会结束线程。(线程资源释放)在异常处理中,由用户确定是否需要中断线程

4.5 线程优先级setPriority

设置线程优先级,线程优先级1~10,默认值是5。操作系统决定,优先级不一定起作用。

4.6 守护线程setDaemon

用户线程。

守护线程:默认false。

守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。

所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

守护线程的finally也不一定会执行

轮询带来的资源消耗,代码不优雅。

五、线程同步

5.1 synchronized

线程安全共享。

synchronized内置锁,在某个时刻只有一个线程访问方法或者同步代码块。

本质:加锁加在对象的对象头上。锁的是对象。

类锁:静态方法上加锁。Class对象加锁。

5.1.1 错误加锁的原因

System.identityHashCode(): 返回原生的hashCode,即使用户的继承类实现了hashCode.

Integer.valueOf返回值是创建了一个新的对象。

synchronized(i):虽然加锁了,但是每次加锁的对象发生了变化,锁在不同的对象上,因此没有启起作用。

需要保证锁的对象不会发生变化

5.2 volatile关键字

volatile保证对象的可见性。一个线程修改了对象的值,这个新值保证可以被其他线程马上看到。

5.3 ThreadLocal

多线程环境下对对象的安全访问。

ThreadLocal为每个线程提供变量副本,实现线程的隔离。

5.3.1 ThreadLocal使用

声明ThreadLocal变量,创建的时候可以指定初始值。

private static ThreadLocal<Integer> threadlocal = new ThreadLocal<Integer>() {

        return 1;

};

获取值get方法:

threadlocal.get()

写入Thread的值:

threadlocal.set()

5.3.2 原理

Map<threadId, Object>

Thread都有一个成员变量 threadlocalMap, 

5.3.2 ThreadLocal内存泄漏问题

ThreadLocal在get方法会清除Thread 回收的键值对,

ThreadLocal持有的对象不能是static,否则对象会出现共享。

 5.3.3 线程多个ThreadLocal怎么储存?

5.4 线程协作

wait/notify、notifyAll

wait和notify的标准范式:

一定包裹在synchronized关键字的范围内。

 

一个线程调用wait方法以后,会把线程自己持有的锁释放掉。

当线程被唤醒以后,会重新去竞争锁。

5.5 等待超时

wait(long timeoutMilSec)

notify:唤醒一个等待线程

notifyALL:唤醒所有的等待进程。

yeild: 让出CPU的执行权,不会释放锁。

sleep: 当前线程休眠一定时间,同样也不会释放锁;

wait: 当前线程进入休眠状态,且释放锁。线程只有唤醒后才能恢复执行。

六、分而治之和归并排序

6.1 Fork-Join

分而治之:把一个大问题分隔成相同的小问题,这个小问题之间无关联。

归并排序、快速排序、二分查找都是采用了分而治之的思想。

6.1.1 归并排序

 实现范式

七、线程的状态

线程的状态也称为线程的生命周期。

初始状态:new一个新的线程对象。并不代表线程真正开始执行。调用start方法以后才开始真正执行。

运行状态:运行态分为就绪态和运行中状态。

运行中:当前线程分配了时间片,拿到了CPU执行权限,进入运行中状态。

就绪态:如果CPU时间片用完了,或者某种原因被CPU剥夺了,或者某种原因放弃了,当前线程进入就绪态。等待操作系统分配时间片。

等待态:wait/join/LockSupport.park,线程进入等待状态。

什么时候从等待状态切回运行态呢?

Object.notify()/Object.notifyAll()/LockSupport.unpark(),可以将线程从等待状态切回运行状态。

如果是等待超时,会自动从等待态切回运行态。

阻塞态:等待进入synchronized,未获取到锁,阻塞等待。

什么时候退出阻塞态?

获取到锁以后,切回运行态。

终止态:线程执行完成。

如果调用显示锁lock,是否进入阻塞态?

不会。一个线程进入阻塞态,只有调用synchronized关键字。显示锁底层使用的是Locksupport。

八、死锁

8.1 什么是死锁?

死锁是指两个或者两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的阻塞现象,若无外力作用,它们都将无法推进下去,此时系统处于死锁状态或者系统产生了死锁。

8.2 发生死锁的条件

发生死锁的三个必要条件:

  1. 多个操作者(M>=2) 争夺多个资源(N》=2),N<=M
  2. 争夺资源顺序不对
  3. 拿到资源不放手

学术化的条件:

  1. 互斥条件
  2. 请求保持
  3. 不剥夺:拿到资源后不能被外界强制剥夺
  4. 环路等待:操作者对资源的等待形成环路。

 8.3 打破死锁的方式

针对资源顺序不会:强制规定争夺资源的顺序;

针对拿到资源不放手:争夺下一个资源失败的情况下,可以释放所有拿到的资源。

8.4 活锁

线程T1和T2 争夺资源A1、A2, T1先拿资源A1、再拿A2; 线程T2先拿资源A2, 再拿A1。两者都采用拿到资源失败时释放拿到的资源。

T1和T2都没有拿到资源,且未产生死锁,这种情况称为活锁。

Sleep一段时间,减少操作者碰撞的概率。

九、线程饥饿

线程优先级太低,老是拿不到CPU执行权。

相关推荐

  1. 进程线

    2024-06-10 10:30:02       22 阅读
  2. 线进程

    2024-06-10 10:30:02       54 阅读
  3. C#理解进程线任务

    2024-06-10 10:30:02       46 阅读
  4. 进程线的区别

    2024-06-10 10:30:02       43 阅读
  5. 进程线

    2024-06-10 10:30:02       42 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-10 10:30:02       91 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-10 10:30:02       97 阅读
  3. 在Django里面运行非项目文件

    2024-06-10 10:30:02       78 阅读
  4. Python语言-面向对象

    2024-06-10 10:30:02       88 阅读

热门阅读

  1. npm发布自己的插件包

    2024-06-10 10:30:02       28 阅读
  2. Servlet 调试

    2024-06-10 10:30:02       25 阅读
  3. TCP复用:原理、应用与优势

    2024-06-10 10:30:02       29 阅读
  4. 【学习笔记】linux解压缩文件小记

    2024-06-10 10:30:02       28 阅读
  5. ObjectARX打印当前图纸为PDF(亲测有效)

    2024-06-10 10:30:02       32 阅读
  6. PDF格式分析(八十四)——小部件注释(Widget)

    2024-06-10 10:30:02       26 阅读
  7. Docker in Docker(DinD)原理与实践

    2024-06-10 10:30:02       25 阅读