线程与进程简单认识

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

官方的解释:进程是资源管理的最小单位,线程是程序执行的最小单元。

也可以理解为进程是计算机运行中的一个应用程序。线程是执行该进程的逻辑单元。例如当我们打开qq时,其实就是开启一个进程,我们可以打字,或者发语音等等,都是有多个线程执行的。

2.进程与线程之间的关系?

举个例子:

现在有一个工厂,工厂里面有一个发电厂,这个发电厂只能一次性提供给一个车间发电(这个车间就是计算机的cpu,一次只能提供给一个车间服务),其他车间就必须在等待中,假设这个车间里面有许多员工(这些员工就是线程),这些员工可以自由的进出车间(线程共享进程的内存),但是厕所一次只能进入一个人,当其他人看到这个厕所里面发现门被锁上时,只能在门口等到,直到这个人出来(这其实就是互斥锁,防止多个线程访问这个内存),但是在工厂里面有个实验室,这个实验室一次性只能做够5个人,如果大于5个人,就在外面等待(那么如何不让这些人发生冲突呢?  可以在外面墙上放5把钥匙,进去的人把门打开,钥匙也带进去,当出来的时候把钥匙放在墙上。在外面等的人看到钥匙,就拿着这个钥匙进去,这就是信号量)。

分析:

1.通过上述例子可能对进程和线程有了一定了解,也可以看出进程有内存地址,线程没有自己的内存地址

2.线程可以共享进程内存地址(这样会不带来问题?)

3.互斥锁(虽然可以解决多个线程访问这个内存,但是会不会存在上厕所的人一直不出来,产生死锁的问题,这样我们如何解决)

4.信号量:(它与互斥锁,临界区关系)?

临界区和互斥锁的相同部分:就是一次只能有一个线程或者一个进程访问公共资源

信号量:一次可以有多个线程访问公共资源

临界区:只能是同进程间的线程互斥 ,非内核对象,速度快。

互斥锁:可以是进程间或者线程间互斥,内核对象,速度慢。

5.线程之间的通信:在进程中使用全局变量。

6.线程与线程之间如何做到同步:互斥锁,信号量,信号量。

6.进程之间的通信:管道,套接字

3.为什么需要多线程?

多线程可以提高高效的使用CPU,但是CPU需要对这些线程进行操作,对不同的系统使用不同的策略,在windows系统中使用抢占式,unix使用的是时间片段法,什么是抢占式,什么是时间片段法?

例子:现在有一块蛋糕,cpu就是分配蛋糕的那个人,有a,b,c三个人想分这块蛋糕,假设cpu说使用时间片段法,规定每一个人的使用时间是1s,那么现在a,b,c就必须按照顺序去吃蛋糕,但是a说我还没吃饱,但是cpu说,不行因为你的时间片段到了,需要轮到下一个人了,如果这是cpu使用抢占式的策略,CPU会根据这几人的优先级和饥饿度来综合考虑,他们的优先级来分配蛋糕,

综上可以看出:无论是抢占式还是时间片段法,都有各自的缺点,抢占式会产生的问题是,有的人优先级非常高,但是它不是饥饿,有的人非常饿,但是有的人优先级非常低,cpu综合计算过后,发现还是优先级高的分到了蛋糕,但是饥饿的人还是很饿,造成线程饥饿,时间片段法的缺点就是还没有吃饱,就到时间了。

4.线程与内存之间的关系

java内存模型分为:主内存和工作内存,那么主内存主要是存储所有变量,主内存也是虚拟机内存的一部分,虚拟机是计算机物理内存的一部分。工作内存是每一个线程都会有的,每一个线程都会从主线程里面取出变量作为副本,来进行有关的逻辑操作。

如果对公共资源进行修改时,就会产生线程问题。

例如:

1. 实例变量且只有一个实例对象

日志:

2. 静态变量

日志:

3.实例变量且各自实例对象

日志:

分析:通过上述的三个例子可以看出,我们发现上述1,2存在线程问题,3存在问题,那么问什么1,2会存在线程问题呢,主要是1里面的实例对象只有一个,可以被多个线程的所获取,并且进行修改,2主要是因为是静态变量,是一个公共资源,这样的话,就会产生被一个线程修改,在自己的工作内存,没有刷新到主内存,且各线程内存之间不能进行交流。3正确是因为每一个实例对象产生一个线程。

5.线程的特性

原子性:java内存模型已经对原子性做了基础保证。如果还需要更细致的保证原子性,内存模型需要使用lock和unlock来完成,但是虚拟机没有把这两个操作给我们使用,在底层使用了monitorenter和moniterexit指令来完成,给我们使用的是synchronized关键字来使用。

  • read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
  • write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。

可见性:解决主内存和工作内存的可见性问题,通过使用volatile关键字,它可以把发生变化的变量刷新回主内存,保证每一个

线程对这个变量都是可见的。

有序性:当我们从一个线程观察我们的程序时,可能是有序的,但是如果是多个线程,可能我们的程序出现无序性,可以在我们的程序中容易出现无序性的地方,使用synchronized关键字。

6.线程的状态

1.新建状态:当我们产生一个new Thread()时,将会新建一个状态。

2.就绪状态:就是调用start方法时,和调用yield方法,将使这个线程进入就绪状态。

3.运行状态:主要根据cpu的调度,在java虚拟机使用的是抢占式策略。

4.阻塞状态:调用sleep和wait都会使线程进入阻塞状态。

5.死亡状态:线程的死亡主要是根据异常和程序的执行完成。

7.线程的api简单理解

1. sleep方法:

是Thread类里面的静态方法,可以在任何地方使用,方法里面必须传入毫秒值,如果在同步代码块里面使用,不会释放锁对象,那么这块代码,将不会被访问,那么问题来了,如果在方法里面传入0毫秒值,这有何意义,如果不在同步代码块中,它的意义就是让cpu放弃这个线程,可以调度其他线程。

2.wait方法和notify方法

这个方法是Object类型的,只能在同步代码块使用,wait()可以传入参数,也可以不传入参数,wait会释放锁,一般情况下,wait

方法和notify方法结合在一起使用。当传入参数,它会在多少秒之后,执行后面的代码,不需要notify进行唤醒。

3.yield方法和jion方法

yield是让出cpu使用的权,是静态方法,如何让出cpu还在研究,jion主要是阻塞主线程运行子线程,主要应用在子线程运算量比较大且比较耗时,等子线程运行完毕后,把结果返回给主线程。

 

猜你喜欢

转载自blog.csdn.net/weixin_41629878/article/details/83829267