进程、线程、多线程

一、基本概念

1、进程(process)

狭义定义:进程就是一段程序的执行过程。

广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

简单的来讲进程的概念主要有两点:

       (1)进程是一个实体,每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储活动过程调用的指令和本地变量。

         (2)进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。

进程状态:

        进程有三个状态:就绪、运行、阻塞。

        就绪状态其实就是获取了除cpu外的所有资源,只要处理器分配资源就可以马上执行。就绪状态有排队队列。

        运行状态就是获得了处理器分配的资源,程序开始执行。

        阻塞状态,当程序条件不够时,需要等待条件满足的时候才能执行,如等待i/o操作时候,此刻的状态就叫做阻塞。

2、程序 

        程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程则是在处理机上的一次执行过程,它是一个动态的概念。这个不难理解,其实进程是包含程序的,进程的执行离不开程序,进程中的文本区域就是代码区,也就是程序。

3、线程

        通常在一个进程可以包含若干个线程,一个进程至少拥有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小基本上不拥有系统资源,故对它的调度所付出的开销就会小很多,能更高效的提高系统多个程序见并发执行的程度。

4、多线程

        在一个程序中,这些独立运行的程序片段叫做线程(Thread),利用它编程的概念就叫“多线程处理”。多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

        最简单的比喻多线程就像是一列火车的每一节车厢,而进程就是火车。车厢离开火车是无法跑动的,同理火车也不可能只有一节车厢。多线程的出现就是为了提高效率。

二、区别 

        进程与线程的区别:

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

      (1)简而言之,一个程序至少有一个进程,一个进程至少有一个线程。

      (2)线程的划分尺度小于进程,使得多线程程序的并发性高。

      (3)另外,进程在执行过程中拥有独立的单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

      (4)线程在执行过程中和进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

      (5)从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看作多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

三、优缺点

        线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程相反。

        同时,线程适合于在SMP(多核处理机)机器上运行,而进程则可以跨机器迁移。

四、多线程实例

分别用Runnable和Thread方法实现,展示各个方法的不同

1、SourceA.java 

package test;

import java.util.ArrayList;
import java.util.List;

public class SourceA {
    private List<String> list = new ArrayList<String>();
    
    public synchronized void getSource(){
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
    public synchronized void setSource(String id){
        list.add(id);
    }
}

2、TestRunnable.java

package test;

public class TestRunnable implements Runnable {

    private int time=1;
    private SourceA s;
    private String id = "001";
    public TestRunnable(SourceA s){
        this.s = s;
    }
    public void setTime(int time) {
        this.time = time;
    }
    
    @Override
    public void run() {
        try {
            System.out.println("i will sleep"+ time);
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized(s){
            s.notify();
            System.out.println("我唤醒了002!");
            System.out.println("我存入了id"+id);
            s.setSource(id);
        }
    }

}

3、TestThread.java

package test;

public class TestThread extends Thread {
    private int time = 1;
    private SourceA s = null;
    String id = "002";
    
    public void setTime(int time) {
        this.time = time;
    }
    
    public TestThread(SourceA s){
        this.s = s ;
    }
    
    @Override
    public void run() {
        try {
            System.out.println("i will sleep"+ time);
            sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        synchronized(s){
            try {
                System.out.println("我"+ id +"要进行等待了");
                s.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我被唤醒了");
            System.out.println("我存入了id"+id);
            s.setSource(id);
        }
    }   
    public static void main(String[] args) {
            SourceA s = new SourceA();
            TestThread tt = new TestThread(s);
            TestRunnable tr = new TestRunnable(s);
            Thread t = new Thread(tr);
            System.out.println("调用线程1");
            tt.start();
            System.out.println("调用线程2");
            t.start();
	}

}

        当某个资源被synchronized所修饰,线程1线程2等多个线程在共同请求这个资源,线程1先请求到,调用了对象的wait方法释放了对象的锁,此时线程2可以对这个对象进行访问,在工作结束时可以调用对象的notify方法,唤醒等待队列中正在等待的线程,此时被唤醒的线程将会再一次拿到对象锁,对对象进行操作。可以调用notifyAll方法,唤醒等待队列中的所有线程。

        需要注意的是一个线程被唤醒不代表立即获取对象锁,必须等调用的线程对象的方法推出synchronized块释放对象锁后,被唤醒的进程才会获得对象锁。

猜你喜欢

转载自blog.csdn.net/DGHxj_/article/details/81362026