Java多线程学习(一)

时间:2021-12-25 18:21:47
Java并发与多线程

摘要:

1. 并发与并行的区别,何为并发编程,并发编程的优势在哪

2. 多线程、多任务、多进程机制概述

3. 多线程、多任务、多进程机制与编程思想的关系

一、并发

1.1 并发与并行

首先介绍一下并发与并行,两者虽然只有一字之差,但实际上却有着本质的区别,其概念如下:

并行性(parallel):指在同一时刻,有多条指令在多个处理器上同时执行;

并发性(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。

1.2 顺序编程与并发编程

我们在解决编程问题时,通常使用顺序编程来解决,即程序中的所有事物在任意时刻都只能执行一个步骤。然而对于某些问题,我们希望能够并行地执行程序中的多个部分,来达到我们想要的效果。在单处理器机器中,我们可以将程序划分为多个部分,然后每个部分由该处理器并发执行。在多处理器机器中,我们可以将程序划分多个部分,然后每个部分分别在多个处理器上并行执行。当然为了更加充分利用CPU资源,我们也可以在多个处理器上并发执行,那么在这我们就涉及到了另一种编程模式了并发编程。并发编程又叫多线程编程。并发编程使我们可以将程序划分为多个分离的、独立运行的任务。通过使用多线程机制,每个独立任务都将由线程来驱动。一个线程就是在进程中的一个单一的顺序控制流,单个进程可以拥有多个"并发执行"的任务。这样使程序的每个任务,都好像拥有一个自己的CPU一样。但其底层机制还是是切分CPU时间,CPU都有个时钟频率,表示每秒中能执行CPU指令的次数。在每个时钟周期内,CPU实际上只能去执行一条也有可能多条指令。操作系统将进程进行管理,轮流分配每个进程很短的一段是时间但不一定是均分,然后在每个进程内部,程序代码自己处理该进程内部线程的时间分配,多个线程之间相互的切换去执行,这个切换时间也是非常短的所以通常我们不需要考虑它。

并发是指"发",不是处理,最常见的情况就是许多人在一小段时间内都点击了你的网站,发出了处理请求。并发编程是对并发状况的应对,在单处理器和多处理器机器上都可对其进行应对,可这个处理方案和架构以及算法有关。CPU一般是分时的,会在极短的时间内不停地切换给不同的线程使用,无论多少并发都会处理下去,只是时间问题,如何提高处理效率就看采用的技术了。

1.3 并发编程的优势

并发编程可以使我们的程序执行速度得到提高,例如,如果你有一台多处理器的机器,那么就可以在这些处理器之间分布多个任务,从而可以极大地提高吞吐量。这是Web服务器的常见情况,一般Web服务器是一个多处理器机器,将为每个请求分配到一个线程中,那么就可以将大量的用户请求分布到多个CPU上进行并发处理。

但是,并发通常是提高运行在单处理器上的程序的性能。虽然,在单处理器上运行的并发程序开销确实应该比该程序的所有部分都顺序执行开销,因为其中增加了所谓上"下文切换"的代价,即从一个任务切换到另一个任务。表面上看,将程序的所有部分当作单个的任务运行好像是开销更小一点,并且可以节省上下文切换的代价。但是我们的程序并不会按我们设想的那样一直正常运行,它会发生阻塞。如果程序中的某个任务因为某些原因发生了阻塞,那么该任务将不能继续执行。如果没有并发,则整个程序都将停止下来,直至外部条件发生变化。但是,如果使用并发来编写程序,那么当一个任务阻塞时,程序中的其他任务还可以继续执行,因此这个程序可以保持继续向前执行,这样就提高程序的执行效率和运行性能。

并发需要付出代价,包含复杂性代价,但是这些代价与在程序设计、资源负载均衡以及用户方便使用方面的改进相比,就显得微不足道了。通常,线程使你能够创建更加松散耦合的设计则,你的代码中各个部分都必须显式地关注那些通常可以由线程来处理的任务。

二、多任务、多进程、多线程

几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程

2.1 多进程

实现并发最直接的方式是在操作系统级别使用进程,进程是运行在它自己的地址空间内的自包容的程序。多任务操作系统可以通过周期性地将CPU从一个进程切换到另一个进程,来实现同时运行多个进程。 尽管对于一个CPU而言,它在某个时间点只能运行一个进程,但CPU可以在多个进程之间进行轮换执行,并且CPU的切换速度极高,使我们无法感知其切换的过程,就好像有多个进程在同时执行。

几乎所有的操作系统都支持进程的概念,所有运行中的任务通常对应一个进程(Process)。当一个程序进入内存运行时,即变成一个进程。进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配调度的一个独立单位。一般而言,进程包含如下3个特征

独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。

动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念,进程具有自己的生命周期和各种不同的状态,这些概念在程序中部是不具备的。

并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。

现代的操作系统都支持多进程的并发,但在具体的实现细节上可能因为硬件和操作系统的不同而采用不同的策略。比较常用的方式有:共用式的多任务操作策略,例如Windows 3.1和Mac OS 9。目前操作系统大多采用效率更高的抢占式多任务操作策略,例如 VVindows NT、Windows 2000以及UNIX/Linux等操作系统。但对进程的并发通常会有数量和开销的限制,以避免它们在不同的并发系统之间的可应用性。为了应对该问题,所以在多进程的基础上提出了多线程的概念,下面将详细介绍。

2.2 多线程

2.2.1 多线程概述

多线程则扩展了多进程的概念。使得同一个进程中也可以同时并发处理多个任务。线程(Thread)也被称作轻量级进程(Lightweight Process)。线程是进程的执行单元,就像进程在操作系统中的地位一样,线程在程序中是独立的、并发的执行流。当进程被初始化后,主线程就被创建了。对于绝大多数的应用程序来说,通常仅要求有一个主线程,但也可以在该进程内创建多条顺序执行流,这些顺序执行流就是线程,每个线程也是互相独立的。

线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。因为多个线程共享父进程里的全部资源,因此编程更加方便;但必须更加小心,我们必须确保线程不会妨碍同一进程里的其他线程。

2.2.2 多线程机制

线程模型为编程带来了便利,它简化了在单一程序中同时交织在一起的多个操作的处理。在使用线程时,CPU将轮流给每个任务分配其占用时间。每个任务都觉得自己在一直占用CPU,但事实上CPU时间是划分成片段分配给了所有的任务。线程的一大好处是可以使你从这个层次抽身出来,即代码不必知道它是运行在具有一个还是多个CPU的机器上。所以,使用线程机制是一种建立透明的、可扩展的程序的方法,如果程序行得太慢,为机器增添一个CPU就能很容易地加快程序的运行速度。多任务和多线程往往是使用多处理器系统的最合理方式。

2.2.3 多线程调度

线程可以完成一定的任务,可以与其他线程共享父进程中的共享变量部分环境,相互之间协同来完成进程所要完成的任务。线程是独立运行的,它并不知道进程中是否还有其他线程存在,线程的执行抢占式的,也就是说,当前运行的线程在任何时候都可能被挂起,以便另外一个线程可以运行。

一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。从逻辑角度来看,多线程存在于一个应用程序中,让一个应用程序中可以有多个执行部分同时执行,但操作系统无须将多个线程看作多个独立的应用,对多线程实现调度和管理以及资源分配。线程的调度管理进程本身负责完成。

归纳起采可以这样说:操作系统可以同时执行多个任务,每个任务就是进程;进程可以同时执行多个任务,每个任务就是线程。简而言之,一个程序运行后至少有一个进程,一个进程里可以包含多个线程,但至少要包含一个线程。

2.2.4 多线程的优势

线程在程序中是独立的、并发的执行流,与分隔的进程相比,进程中线程之间的隔离程度要小

01. 它们共享内存文件句柄和其他每个进程应有的状态。因为线程的划分尺度小于进程,使得多线程程序的并发性高。进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率

02. 线程比进程具有更高的性能,这是由于同一个进程中的线程都有共性----多个线程共享同一个进程虚拟空间。线程共享的环境包括进程代码段进程的公有数据等。利用这些共享的数据,线程很容易实现相互之间的通信

03. 当操作系统创建一个进程时,必须为该进程分配独立的内存空间,并分配大量的相关资源;但创建一个线程则简单得多,因此使用多线程来实现并发比使用多进程实现并发的性能要高得多

总结起来,使用多线程编程具有如下几个优点:

01. 进程之间不能共享内存,但线程之间共享内存非常容易

02. 系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高

03. Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程

在实际应用中,多线程是非常有用的,一个浏览器必须能同时下载多个图片;一个Web服务器必须能同时响应多个用户请求;Java虚拟机本身就在后台提供了一个超级线程来进行垃圾回收;图形用户界面(GUI)应用也需要启动单独的线程从主机环境收集用户界面事件……总之,多线程在实际编程中的应用是非常广泛的。

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【Sunddenly】。

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利