进程与线程

来源:互联网 发布:吉利知豆d2说明书 编辑:程序博客网 时间:2024/04/27 15:58


推荐一篇写的比较通俗的文章:进程与线程的一个简单解释


内容:

1.计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。(CPU --- 工厂(供电))


2.假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务


3.进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。


4.一个车间里,可以有很多工人。他们协同完成一个任务。


5.线程就好比车间里的工人。一个进程可以包括多个线程。


6.车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。


7.可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。


8.一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。


9.还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。


10.这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。


不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。


11.操作系统的设计,因此可以归结为三点:
(1)以多进程形式,允许多个任务同时运行;
(2)以多线程形式,允许单个任务分成不同的部分运行;
(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。


进程(程序的一次执行)

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。应用程序(application)是由一个或多个相互协作的进程组成的。

程序和进程的区别就在于:

程序是指令的集合,它是进程运行的静态描述文本;

进程是程序的一次执行活动,属于动态概念。


在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。


有了进程为什么还要线程?

(1)进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。
(2)进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。



线程(CPU的基本调度单位)

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.

线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。主线程将执行运行时宿主, 而运行时宿主会负责载入CLR。


一个线程包含以下内容
一个指向当前被执行指令的指令指针;
一个栈;
一个寄存器值的集合,定义了一部分描述正在执行线程的处理器状态的值;
一个私有的数据区。


java中线程的几种状态:

java thread的运行周期中, 有几种状态, 在 java.lang.Thread.State 中有详细定义和说明:

NEW(产生) :状态是指线程刚创建, 尚未启动,通过new产生对象后没有对它调用start()方法

RUNNABLE(可执行): 它可能处于线程池中等待排程器启动它;也有可能它正在执行。如执行了一个线程对象的start()方法后,线程就处于可执行状态。但是,显然线程可能并不是在执行中。
 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等。


BLOCKED (阻塞): 这个状态下, 是在多个线程有同步操作的场景,通常是它在等待着一个锁! 比如正在等待另一个线程的synchronized 块的执行释放, 或者可重入的 synchronized块里别人调用wait() 方法, 也就是这里是线程在等待进入临界区。


WAITING : 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify(通知) / notifyAll 一遍该线程可以继续下一步操作, 这里要区分BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在临界点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束


TIMED_WAITING:  这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态

TERMINATED(终止): 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)


操作系统中线程状态:

第一是创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。

 
第二是就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。 


第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。

 
第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。 


第五是死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪。


进程与线程的区别
(1)进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体, 是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。


(2)一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。


(3)进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间.在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。


补充知识:

进程同步与互斥基础

同步:
为了完成某个任务而建立的两个或多个进程,这些进程为了需要在某些位置上协调它们的工作次序而等待、传递信息所产生的制约关系。进程间的制约关系就是源于它们之间的相互合作。

互斥:当一个进程进入临界区使用临界资源是,另一个进程必须等待,当占用临界区资源的进程退出临界区以后才允许进入临界区,访问资源。注意,这里的一个并非卡死就一次只能一个进程访问临界资源。当信号量个数大于1的时候,一次可以有多个进程同时访问临界资源。前文的互斥定义只是最原始的定义。

二、同步准则
1)空闲让进:临界区空闲时,应该允许某个进程立即进入临界区访问临界资源。

2)忙则等待:当临界区进程访问数达到方位上限时,其他试图进入临界区的进程应该等待。

3)有限等待:应该避免进程一直等待永远进入不了临界区的情况。

4)让权等待:“权”即CPU使用权,当进程不能进入临界区的时候,应该放弃使用CPU,进入等待队列,而不是“忙等待”。


线程之间的关系大致可以分为两类
1、线程之间通过对资源的竞争,包括共享的数据和硬件资源,所产生的相互制约关系,这类线程间的主要问题是互斥和死锁问题,这类关系被称为互斥关系
2、线程之间的相互协同合作,彼此之间直接知道对方的存在,并了解对方的名字,这类进程常常需要通过“进程间通信”方法来协同工作,这类关系被称为同步关系
--共享资源是指在程序中并发运行的 若干个线程所操作的同一数据资源。
--并发运行的若干线程在操作共享资源时可能会出错,通常把这种现象称为非线程安全。反之,则称为线程安全


--线程间的互斥
出现共享资源访问冲突的实质是线程没有互斥的使用共享资源,即单个线程需要独占性的使用共享资源。
共享资源也称为“临界资源”,而将线程中访问共享变量的代码段称为临界段。为了使系统中并行线程正确而有效的访问资源,对线程互斥使用临界段有以下原则:
1、在共享同一个临界资源的所有线程中,每次只允许有一个线程处于它的临界段之中。也就是说强制所有这些线程中,每次只允许其中的一个线程访问该共享变量。
2、线程只应在临界段内逗留有限时间。
3、若有多个线程同时要求进入它们的临界段时,就在有限的时间内让其中之一进入临界段,而不应相互阻塞,以至于各线程均无法进入临界段。
为了能够对临界段实现互斥,计算机科学家提出使用“同步愿语”来访问“信号量”的方式来解决。
信号量是指用于协调多个相互关联线程操作的信号,当一个线程需要访问临界段时,将被强制地停在临界段的访问入口处,直到收到一个专门的信号,才能进入临界段。
同步原语,通常用来保证临界段代码执行的原子性,即不可中断性。


关键字:synchronized(同步)

多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题。同步机制可以使用synchronized关键字实现。当synchronized关键字修饰一个方法的时候,该方法叫做同步方法


1.普通方法前面加synchronized

(1)如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。

(2)Java中的每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时,将该对象上锁,其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法),直到之前的那个线程执行方法完毕后(或者是抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。

(3)注意这时候是给对象上锁,如果是不同的对象,则各个对象之间没有限制关系.


2.静态方法前面加synchronized

(1)当一个synchronized关键字修饰的方法同时又被static修饰,非静态的同步方法会将对象上锁,但是静态方法不属于对象,而是属于类,它会将这个方法所在的类的Class对象上锁。一个类不管生成多少个对象,它们所对应的是同一个Class对象。

(2)如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的类所对应的Class对象。Java中,无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,它们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始。


3.synchronized块

synchronized块写法:
  synchronized(object)
  {      

  }
表示线程在执行的时候会将object对象上锁。(注意这个对象可以是任意类的对象,也可以使用this关键字)。


可能一个方法中只有几行代码会涉及到线程同步问题,所以synchronized块比synchronized方法更加细粒度地控制了多个线程的访问,只有synchronized块中的内容不能同时被多个线程所访问,方法中的其他语句仍然可以同时被多个线程所访问(包括synchronized块之前的和之后的)。
注意:被synchronized保护的数据应该是私有的。


结论:
  synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;
  synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的其他代码是可以被多个线程同时访问到的。


参考来源:  进程与线程及其区别 Java并发编程之同步互斥问题 Java中线程的互斥与同步    进程同步与互斥基础   Java 多线程(六) synchronized关键字详解




0 0
原创粉丝点击