并发基础

1.什么是线程和进程

1.1 什么是进程

进程就是程序的一次执行过程,是运行程序的基本单位。所以,进程是动态的,程序的运行就是进程从创建到灭亡的过程。
比如,main函数运行时,就是虚拟机启动了一个进程,main函数所在的线程就是这个进程的一个主线程.

1.2 什么是线程

线程与进程类似,但线程是比进程更小的单位,一个进程中可以有多个线程,与进程不同的是同类的多个线程可以共享堆和方法区的资源,但因为每个线程有私有的程序计数器,虚拟机栈和本地方法栈,所以系统在产生一个线程,在线程之间切换时,压力要比进程小的多,所以,线程也被称为轻量级的进程。

一个java程序的运行是main线程和其他多个线程同时运行。

2.线程与进程的区别,关系和优缺点

从内存区域中,我们可以了解到,一个进程可以有多个线程,多个线程共享堆和方法区,但是每个线程都有私有的程序计数器,本地方法栈,虚拟机栈。

线程是进程的更小的单位,线程和进程的最大不同在于,每个进程是独立的,而线程不一定,因为一个进程中的线程可能会相互影响。线程的开销比较小,但是不利于资源的管理和维护。

2.1 程序计数器为什么是私有的

从内存的了解中,我们知道程序计数器有两个作用:
1.字节码解释器通过程序计数器来依次读取指令,实现流程控制,比如:顺序执行,选择,循环等。
2. 当处于多线程时,计数器记录当前线程执行的位置,当进行切换时,可以知道线程的位置在哪。

所以程序计数器私有化的目的是使线程可以切换后,恢复到正确的位置。

2.2 虚拟机栈和本地方法栈为什么是私有的

虚拟机栈:每一个java方法在执行的时候,会创建一个栈帧存储局部变量表,常量池等信息,一直到方法的结束,栈帧会在进行入栈出栈。
本地方法栈:与虚拟机栈的作用相似,在HotSpot中将这两个合二为一。

所以私有的目的是:保证线程中的局部变量不被其他线程访问

2.3 堆和方法区

这两个是线程共享的资源,堆主要存放新创建的对象,方法区主要存储常量,静态变量等。

3.并发和并行的区别

**并发:**同一个时间范围内,多个人任务都在执行
并行:单位时间内,多个任务同时执行

4.为什么使用多线程

1.计算机底层:线程是轻量级的基础,开销小,多核cpu可以保证多个线程同时运行。
2.目前互联网的高并发,多线程是高并发的基础

5.多线程可能带来的问题

并发编程可以提高效率和运行速度,但是并发编程可能会遇到一些问题,比如:内存泄漏,死锁,上下文切换等问题。

6.线程的生命周期和状态

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。
在线程的生命周期中,它要经过新建(New)就绪(Runnable)运行(Running)阻塞(Blocked)和**死亡(Dead)**5 种状态。尤其是当线程启动以后,它不可能一直"霸占"着 CPU 独自运行,所以 CPU 需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换

在这里插入图片描述

6.1 新建状态(NEW)

当程序使用 new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配
内存,并初始化其成员变量的值

6.2 就绪状态(RUNNABLE)

当线程对象调用了 start()方法之后,该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和
程序计数器,等待调度运行。

6.3 运行状态(RUNNING)

如果处于就绪状态的线程获得了 CPU时间片,开始执行 run()方法的线程执行体,则该线程处于运行状态。

6.4 阻塞状态(BLOCKED)

阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。
直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状
态。阻塞的情况分三种:

等待阻塞(o.wait->等待队列):
运行(running)的线程执行 o.wait()方法,JVM 会把该线程放入等待队列(waitting queue)中。

同步阻塞(lock->锁池):
运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池(lock pool)中。

其他阻塞(join/sleep):
运行(running)的线程执行 Thread.sleep(long ms)或 t.join()方法,或者发出了 I/O 请求时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时、join()等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入可运行(runnable)状态。

6.5 线程死亡(DEAD)

线程会以下面三种方式结束,结束后就是死亡状态。

正常结束

  1. run()或 call()方法执行完成,线程正常结束。

异常结束
2. 线程抛出一个未捕获的 Exception 或 Error

调用stop
3. 直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。

在这里插入图片描述

7.什么是上下文切换

巧妙地利用了时间片轮转的方式, CPU 给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务,任务的状态保存及再加载, 这段过程就叫做上下文切换。时间片轮转的方式使多个任务在同一颗 CPU 上执行变成了可能。

8.死锁是什么,如何避免

何为死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。在这里插入图片描述

8. sleep()和wait()方法不同和共同点

  1. 对于 sleep()方法,我们首先要知道该方法是属于 Thread 类中的。而 wait()方法,则是属于Object 类中的。
  2. sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
  3. 在调用 sleep()方法的过程中,线程不会释放对象锁。
  4. 而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此
    对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

9.为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

  1. start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕可以直接继续执行下面的代码。
  2. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运
    行。
  3. 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运
    行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值