线程与进程的区别
一、线程的基本概念
线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。
好处:
- 易于调度
- 提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。
- 开销少。创建线程比创建进程要快,所需开销很少。
- 利于充分发挥多处理器的功能。通过创建多线程进程,每个线程在一个处理器上运行,从而实现应用程序的并发性,使每个处理器都得到充分运行。
二、进程的基本状态及状态之间的关系<div align=center
详见博文《 Java进阶(四十一)多线程讲解》。
三、线程和进程的关系以及区别
3.1 进程和线程的关系
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
- 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
- 处理机分给线程,即真正在处理机上运行的是线程。
- 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.
3.2 进程与线程的区别
- 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
- 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行。
- 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
- 系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
四、进程间通信的方式
4.1 管道(pipe)及有名管道(named pipe)
管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系;有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
4.2 信号(signal)
信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。
4.3 信号量(semaphore)
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程之间及同一种进程的不同线程之间的同步和互斥手段。
4.4 消息队列(message queue)
消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。具有写权限的进程可以按照一定的规则向消息队列中添加新信息;对消息队列有读权限的进程则可以从消息队列中读取信息。
4.5 共享内存(shared memory)
可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
4.6 套接字(socket)
这是一种更为一般的进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。
五、线程间通信的方式
5.1 锁机制
包括互斥锁、条件变量、读写锁
- 互斥锁提供了以排他方式防止数据结构被并发修改的方法。
- 读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
- 条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
5.2 信号量机制(Semaphore)
包括无名线程信号量和命名线程信号量。
5.3 信号机制(Signal)
类似进程间的信号处理。
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。
5.4 管道(Pipe)
示例代码如下:
package cn.edu.ujn.demo;
import java.io.PipedReader;
import java.io.PipedWriter;
public class Piped {
public static void main(String[] args) throws Exception{
PipedWriter out = new PipedWriter();
PipedReader in = new PipedReader();
// 连接输入流和输出流,否则会报异常:java.io.IOException: Pipe not connected
out.connect(in);
Thread printThread = new Thread(new Print(in), "PrintThread");
printThread.start();
int receive = 0;
try {
while((receive = System.in.read()) != -1){
out.write(receive);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
out.close();
}
}
}
class Print implements Runnable{
private PipedReader in;
public Print(PipedReader in){
this.in = in;
}
public void run(){
int receive = 0;
try {
while((receive = in.read()) != -1){
System.out.print((char)receive);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
六、同步和互斥的区别
当有多个线程的时候,经常需要去同步这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件写到内存,而另一个线程用于统计文件中的字符数。当然,在把整个文件调入内存之前,统计它的字符数是没有意义的。但是,由于每个操作都有自己的线程,操作系统会把两个线程当作是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字符数。为解决此问题,你必须使两个线程同步工作。
所谓同步,是指散布在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
- 点赞
- 收藏
- 关注作者
评论(0)