精华内容
下载资源
问答
  • 如何让多进程多线程访问数据库,而不会选择相同的数据,这在设计分布式程序的时候经常用到,多台机器的多个进程,每个进程都有多个线程,每个线程要从数据库里取数据来处理,要实现不能漏取数据,也不能重复取数据,...

    如何让多进程多线程访问数据库,而不会选择相同的数据,这在设计分布式程序的时候经常用到,多台机器的多个进程,每个进程都有多个线程,每个线程要从数据库里取数据来处理,要实现不能漏取数据,也不能重复取数据,这里给出答案 .

    创建一个数据表,如下,一个自增列,一个表示rss链接地址先放1w条数据再创建一个锁表,一个字段表示是否已经锁定的资源,另一个表示已经读取的rss源的最大id初始化数据下面我们要设计一个存储过程,让这个存储过程每次返回10个rss源,知道返回所有的rss源,要求无遗漏,无重复返回。如下1、如果锁表里显示没有进程正在读取rss源(IsLock = 0),那么就返回从最大的rss源id往后的10个rss源,否则返回空。

    2、用with(READPAST)表示忽略锁住的行,如果另一个进程正在执行update Rss_RssSourceLock的语句,并且在事务提交前,update语句会锁住这些要更新的行,而Rss_RssSourceLock表就一行数据,这时候select Rss_RssSourceLock表并且忽略被锁的行肯定是没数据的,所以本次存储过程执行会返回空。

    3、begin tran和commit tran保证了即使本次存储过程出错,也不会让Rss_RssSourceLock表处于IsLock = 1的脏数据状态,如果处于这种状态,后面的进程执行存储过程就永远也返回不了数据了。

    4、因为有时候一次选取的记录可能不够10条,所以这里用了个临时表来暂存记录,再算出来选取的条数,最后更新Rss_RssSourceLock表的MaxSourceId字段。但用临时表肯定会增加数据库的压力,这里不知道用表变量是不是会改善性能,暂时先这样了。

    5、应用里调用这个存储过程,如果返回了数据,就进行处理,如果没返回数据,就sleep几秒才执行,直到返回数据。

    CREATE TABLE [dbo].[Rss_RssSources](

    [SourceId] [int] IDENTITY(1,1) NOT NULL,

    [Link] [varchar](1024) NOT NULL

    ) ON [PRIMARY]

    declare @i int

    set @i = 1

    while @i <10000

    begin

    select @i = @i +1

    insert into [Rss_RssSources] values(newid())

    end

    create table Rss_RssSourceLock

    (

    IsLock bit,

    MaxSourceId int

    )

    insert into Rss_RssSourceLock values (0,0)

    CREATE PROCEDURE [dbo].[USP_GetRssSources]

    AS

    BEGIN

    if exists(select * from Rss_RssSourceLock with(READPAST) where IsLock = 0)

    begin

    declare @select_count int

    begin tran

    update Rss_RssSourceLock set IsLock = 1

    if object_id('tempdb..#t') is not null

    drop table #t

    select top 10 a.* into #t from [Rss_RssSources] as a

    inner join Rss_RssSourceLock as b

    on a.SourceId > b.MaxSourceId

    order by a.[SourceId]

    select @select_count = count(*) from #t

    update Rss_RssSourceLock set IsLock = 0,MaxSourceId = MaxSourceId + @select_count

    select * from #t

    commit tran

    end

    END

    2010年12月29日 11:41

    0df68cf8acfe8912e9f42e24e15e7b08.gif

    1328

    0

    0

    0

    展开全文
  • 本文实例总结了Python多进程并发与多线程并发。分享给大家供大家参考,具体如下:这里对python支持的几种并发方式进行简单的总结。Python支持的并发分为多线程并发与多进程并发(异步IO本文不涉及)。概念上来说,多...

    本文实例总结了Python多进程并发与多线程并发。分享给大家供大家参考,具体如下:

    这里对python支持的几种并发方式进行简单的总结。

    Python支持的并发分为多线程并发与多进程并发(异步IO本文不涉及)。概念上来说,多进程并发即运行多个独立的程序,优势在于并发处理的任务都由操作系统管理,不足之处在于程序与各进程之间的通信和数据共享不方便;多线程并发则由程序员管理并发处理的任务,这种并发方式可以方便地在线程间共享数据(前提是不能互斥)。Python对多线程和多进程的支持都比一般编程语言更高级,最小化了需要我们完成的工作。

    一.多进程并发

    Mark Summerfield指出,对于计算密集型程序,多进程并发优于多线程并发。计算密集型程序指的程序的运行时间大部分消耗在CPU的运算处理过程,而硬盘和内存的读写消耗的时间很短;相对地,IO密集型程序指的则是程序的运行时间大部分消耗在硬盘和内存的读写上,CPU的运算时间很短。

    对于多进程并发,python支持两种实现方式,一种是采用进程安全的数据结构:multiprocessing.JoinableQueue,这种数据结构自己管理“加锁”的过程,程序员无需担心“死锁”的问题;python还提供了一种更为优雅而高级的实现方式:采用进程池。下面一一介绍。

    1.队列实现——使用multiprocessing.JoinableQueue

    multiprocessing是python标准库中支持多进程并发的模块,我们这里采用multiprocessing中的数据结构:JoinableQueue,它本质上仍是一个FIFO的队列,它与一般队列(如queue中的Queue)的区别在于它是多进程安全的,这意味着我们不用担心它的互斥和死锁问题。JoinableQueue主要可以用来存放执行的任务和收集任务的执行结果。举例来看(以下皆省去导入包的过程):

    def read(q):

    while True:

    try:

    value = q.get()

    print('Get %s from queue.' % value)

    time.sleep(random.random())

    finally:

    q.task_done()

    def main():

    q = multiprocessing.JoinableQueue()

    pw1 = multiprocessing.Process(target=read, args=(q,))

    pw2 = multiprocessing.Process(target=read, args=(q,))

    pw1.daemon = True

    pw2.daemon = True

    pw1.start()

    pw2.start()

    for c in [chr(ord('A')+i) for i in range(26)]:

    q.put(c)

    try:

    q.join()

    except KeyboardInterrupt:

    print("stopped by hand")

    if __name__ == '__main__':

    main()

    对于windows系统的多进程并发,程序文件里必须含有“入口函数”(如main函数),且结尾处必须调用入口点。例如以if __name__ == '__main__': main()结尾。

    在这个最简单的多进程并发例子里,我们用多进程实现将26个字母打印出来。首先定义一个存放任务的JoinableQueue对象,然后实例化两个Process对象(每个对象对应一个子进程),实例化Process对象需要传送target和args参数,target是实现每个任务工作中的具体函数,args是target函数的参数。

    pw1.daemon = True

    pw2.daemon = True

    这两句话将子进程设置为守护进程——主进程结束后随之结束。

    pw1.start()

    pw2.start()

    一旦运行到这两句话,子进程就开始独立于父进程运行了,它会在单独的进程里调用target引用的函数——在这里即read函数,它是一个死循环,将参数q中的数一一读取并打印出来。

    value = q.get()

    这是多进程并发的要点,q是一个JoinableQueue对象,支持get方法读取第一个元素,如果q中没有元素,进程就会阻塞,直至q中被存入新元素。

    因此执行完pw1.start() pw2.start()这两句话后,子进程虽然开始运行了,但很快就堵塞住。

    for c in [chr(ord('A')+i) for i in range(26)]:

    q.put(c)

    将26个字母依次放入JoinableQueue对象中,这时候两个子进程不再阻塞,开始真正地执行任务。两个子进程都用value = q.get()来读取数据,它们都在修改q对象,而我们并不用担心同步问题,这就是multiProcessing.Joinable数据结构的优势所在——它是多进程安全的,它会自动处理“加锁”的过程。

    try:

    q.join()

    q.join()方法会查询q中的数据是否已读完——这里指的就是任务是否执行完,如果没有,程序会阻塞住等待q中数据读完才开始继续执行(可以用Ctrl+C强制停止)。

    对Windows系统,调用任务管理器应该可以看到有多个子进程在运行。

    2.进程池实现——使用concurrent.futures.ProcessPoolExecutor

    Python还支持一种更为优雅的多进程并发方式,直接看例子:

    def read(q):

    print('Get %s from queue.' % q)

    time.sleep(random.random())

    def main():

    futures = set()

    with concurrent.futures.ProcessPoolExecutor() as executor:

    for q in (chr(ord('A')+i) for i in range(26)):

    future = executor.submit(read, q)

    futures.add(future)

    try:

    for future in concurrent.futures.as_completed(futures):

    err = future.exception()

    if err is not None:

    raise err

    except KeyboardInterrupt:

    print("stopped by hand")

    if __name__ == '__main__':

    main()

    这里我们采用concurrent.futures.ProcessPoolExecutor对象,可以把它想象成一个进程池,子进程往里“填”。我们通过submit方法实例一个Future对象,然后把这里Future对象都填到池——futures里,这里futures是一个set对象。只要进程池里有future,就会开始执行任务。这里的read函数更为简单——只是把一个字符打印并休眠一会而已。

    try:

    for future in concurrent.futures.as_completed(futures):

    这是等待所有子进程都执行完毕。子进程执行过程中可能抛出异常,err = future.exception()可以收集这些异常,便于后期处理。

    可以看出用Future对象处理多进程并发更为简洁,无论是target函数的编写、子进程的启动等等,future对象还可以向使用者汇报其状态,也可以汇报执行结果或执行时的异常。

    二.多线程并发

    对于IO密集型程序,多线程并发可能要优于多进程并发。因为对于网络通信等IO密集型任务来说,决定程序效率的主要是网络延迟,这时候是使用进程还是线程就没有太大关系了。

    1.队列实现——使用queue.Queue

    程序与多进程基本一致,只是这里我们不必使用multiProcessing.JoinableQueue对象了,一般的队列(来自queue.Queue)就可以满足要求:

    def read(q):

    while True:

    try:

    value = q.get()

    print('Get %s from queue.' % value)

    time.sleep(random.random())

    finally:

    q.task_done()

    def main():

    q = queue.Queue()

    pw1 = threading.Thread(target=read, args=(q,))

    pw2 = threading.Thread(target=read, args=(q,))

    pw1.daemon = True

    pw2.daemon = True

    pw1.start()

    pw2.start()

    for c in [chr(ord('A')+i) for i in range(26)]:

    q.put(c)

    try:

    q.join()

    except KeyboardInterrupt:

    print("stopped by hand")

    if __name__ == '__main__':

    main()

    并且这里我们实例化的是Thread对象,而不是Process对象,程序的其余部分看起来与多进程并没有什么两样。

    2. 线程池实现——使用concurrent.futures.ThreadPoolExecutor

    直接看例子:

    def read(q):

    print('Get %s from queue.' % q)

    time.sleep(random.random())

    def main():

    futures = set()

    with concurrent.futures.ThreadPoolExecutor(multiprocessing.cpu_count()*4) as executor:

    for q in (chr(ord('A')+i) for i in range(26)):

    future = executor.submit(read, q)

    futures.add(future)

    try:

    for future in concurrent.futures.as_completed(futures):

    err = future.exception()

    if err is not None:

    raise err

    except KeyboardInterrupt:

    print("stopped by hand")

    if __name__ == '__main__':

    main()

    用ThreadPoolExecutor与用ProcessPoolExecutor看起来没什么区别,只是改了一下签名而已。

    不难看出,不管是使用队列还是使用进/线程池,从多进程转化到多线程是十分容易的——仅仅是修改了几个签名而已。当然内部机制完全不同,只是python的封装非常好,使我们可以不用关心这些细节,这正是python优雅之处。

    希望本文所述对大家Python程序设计有所帮助。

    展开全文
  • 1.并发1.1 并发与并行首先介绍一下并发与并行,两者虽然只有一字之差,但实际上却有着本质的区别,其概念如下:并行性(parallel):指在同一时刻,有条指令在个处理器上同时执行;并发性(concurrency):指在同一...

    1.并发

    1.1 并发与并行

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

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

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

    a1b48e5dc7072b763151be0c25ca086c.png

    在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。

    而在多个 CPU 系统中,则这些可以并发执行的程序便可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序便可以同时执行。目前电脑市场上说的多核 CPU,便是多核处理器,核越多,并行处理的程序越多,能大大的提高电脑运行的效率。

    2406583d3e396ead2f640ede38aa0ea7.png

    注意:单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同理,线程也是一样的,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度。

    1.2 顺序编程与并发编程

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

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

    1.3 并发编程的优势

    如果使用得当,线程可以有效地降低程序的开发和维护等成本,同时提升复杂应用程序的性能。具体说,线程的优势有:

    发挥多处理器的强大能力

    现在,多处理器系统正日益盛行,并且价格不断降低,即时在低端服务器和中断桌面系统中,通常也会采用多个处理器,这种趋势还在进一步加快,因为通过提高时钟频率来提升性能已变得越来越困难,处理器生产厂商都开始转而在单个芯片上放置多个处理器核。试想,如果只有单个线程,双核处理器系统上程序只能使用一半的CPU资源,拥有100个处理器的系统上将有99%的资源无法使用。多线程程序则可以同时在多个处理器上执行,如果设计正确,多线程程序可以通过提高处理器资源的利用率来提升系统吞吐率。

    在单处理器系统上获得更高的吞吐率

    如果程序是单线程的,那么当程序等待某个同步I/O操作完成时,处理器将处于空闲状态。而在多线程程序中,如果一个线程在等待I/O操作完成,另一个线程可以继续运行,使得程序能在I/O阻塞期间继续运行。

    建模的简单性

    通过使用线程,可以将复杂并且异步的工作流进一步分解为一组简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置进行交互。我们可以通过一些现有框架来实现上述目标,例如Servlet和RMI,框架负责解决一些细节问题,例如请求管理、线程创建、负载平衡,并在正确的时候将请求分发给正确的应用程序组件。编写Servlet的开发人员不需要了解多少请求在同一时刻要被处理,也不需要了解套接字的输入流或输出流是否被阻塞,当调用Servlet的service方法来响应Web请求时,可以以同步的方式来处理这个请求,就好像它是一个单线程程序。

    异步事件的简化处理

    服务器应用程序在接受多个来自远程客户端的套接字连接请求时,如果为每个连接都分配其各自的线程并且使用同步I/O,那么就会降低这类程序的开发难度。如果某个应用程序对套接字执行读操作而此时还没有数据到来,那么这个读操作将一直阻塞,直到有数据到达。在单线程应用程序中,这不仅意味着在处理请求的过程中将停顿,而且还意味着在这个线程被阻塞期间,对所有请求的处理都将停顿。为了避免这个问题,单线程服务器应用程序必须使用非阻塞I/O,但是这种I/O的复杂性要远远高于同步I/O,并且很容易出错。然而,如果每个请求都拥有自己的处理线程,那么在处理某个请求时发生的阻塞将不会影响其他请求的处理。

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

    2.进程和线程

    进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。进程可以理解为受操作系统管理的基本运行单元。360浏览器是一个进程、WPS也是一个进程,正在操作系统中运行的".exe"都可以理解为一个进程。

    68cebf1aa0259928fa3615c9fa1c5bbc.png

    线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

    进程中独立运行的子任务就是一个线程。像QQ.exe运行的时候就有很多子任务在运行,比如聊天线程、好友视频线程、下载文件线程等等。

    40eb2b746419b41d98f29d127b0022c8.png

    简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。

    3.多任务、多进程、多线程

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

    3.1 多进程

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

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

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

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

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

    3.2 多线程

    3.2.1 多线程概述

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

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

    3.2.2 多线程机制

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

    3.2.3 多线程调度

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

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

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

    展开全文
  • 进程、线程、多线程并发与并行、线程运行内存模型、锁 1.1我们为什么需要多线程? 比如我们在听歌时可以一边听一边下载,这种一个程序看起来同时做好几件事的情况,就需要多线程。 线程(Thread)和进程(Process)的...

    进程、线程、多线程、并发与并行、线程运行内存模型、锁

    1.1我们为什么需要多线程?
    比如我们在听歌时可以一边听一边下载,这种一个程序看起来同时做好几件事的情况,就需要多线程。

    线程(Thread)和进程(Process)的关系很紧密,进程和线程是两个不同的概念,进程的范围大于线程。通俗地说,进程就是一个程序,线程是这个程序能够同时做的各件事情。比如,媒体播放机运行时就是一个进程,而媒体播放机同时做的下载文件和播放歌曲,就是两个线程。因此,可以说进程包含线程。

    线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所有的全部资源。一个进程中是可以拥有很多线程的。

    1.2实现多线程的方法

    实现多线程有很多方法:Thread 继承 、Runnable 接口实现 、Callable 接口实现(带返回值) 、线程池(提供线程)。
    我们要讲的就是其中的两种: Thread 继承 、Runnable 接口实现

    1.2.1继承Thread 类开发多线程

    我们可以用多线程来解决“程序看起来同时做好几件事情”的效果,只需要将各个文件传送的工作分别写在线程里即可。

    继承 Thread 类实现多线程,该方法步骤如下:
    、编写一个类,继承 java.lang.Thread
    、在这个类中,重写 java.lang.Thread 类中的以下函数:
    public void run()

    class FileTransThread extends Thread{
          private String fileName;
          public FileTransThread(String fileName){ 
              this.fileName = fileName;
          }
          public void run(){
              System.out.println("传送" + fileName); 
                 try{
                    Thread.sleep(1000 * 10);
                    }
                    catch(Exception ex){}
                    System.out.println(fileName + "传送完毕");
          }
     }
    

    线程编写完毕

    、实例化线程对象,调用其 **start()**函数来启动该线程。

    FileTransThread ft1 = new FileTransThread(“文件 1);
    ft1.start();
    

    1.2.2实现Runnable 接口开发多线程

    该方法实现步骤如下:
    、编写一个类,实现 java.lang.Runnable 接口
    、在这个类中,重写 java.lang. Runnable 接口中的以下函数:
    public void run()
    将线程需要执行的代码放入run方法。

    class FileTransRunnable implements Runnable{ 
          private String fileName ;
          public FileTransRunnable(String fileName){ 
              this.fileName = fileName ;
          }
          public void run(){
              System.out.println("传送" + fileName); 
              try{
                   Thread.sleep(1000 * 10);
                 }catch(Exception ex){}
              System.out.println(fileName + "传送完毕");
          }
    }
    

    、实例化 java.lang.Thread 对象,实例化上面编写的 Runnable 实现类,将后者传入 Thread 对象的构造函数。调用 Thread 对象的 start方法来启动线程。

    FileTransRunnable fr1 = new FileTransRunnable("文件 1");
    Thread th1 = new Thread(fr1);
    th1.start();
    

    1.2.3两种方法的不同之处

    、继承 Thread 的方法,具有如下特点:

    每一个对象都是一个线程,其对象具有自己的成员变量。比如:

    class FileTransThread extends Thread{ private String fileName;
    public void run(){
    }
    FileTransThread ft1 = new FileTransThread(); 
    FileTransThread ft2 = new FileTransThread();
    

    此时,线程 ft1ft2 具有各自的 fileName 成员变量,除非将 fileName 定义为静态变量。

    Java 不支持多重继承,继承了 Thread,就不能继承其他类。因此,该类主要完成线程工作,功能比较单一。

    二、实现 Runnable 的方法,具有如下特点:

    每一个对象不是一个线程,必须将其传入 Thread 对象才能运行,各个线程是否共享 Runnable 对象成员,视情况而定。比如有如下 Runnable 类:

    class FileTransRunnable extends Runnable{ 
          private String fileName ;
          public void run(){
          }
    }
    

    下面代码:

    FileTransRunnable fr1 = new FileTransRunnable(); 
    FileTransRunnable fr2 = new FileTransRunnable(); 
    Thread th1 = new Thread(fr1);
    Thread th2 = new Thread(fr2);
    

    此时,线程 th1th2 访问各自的 fileName 成员变量,因为它们传进来的FileTransRunnable 是不同的。但是如下代码:

    FileTransRunnable fr = new FileTransRunnable();
    Thread th1 = new Thread(fr);
    Thread th2 = new Thread(fr);
    

    线程 th1th2 访问的确实同一个 fileName 成员变量,因为它们传进来的FileTransRunnable 是同一个对象。

    Java 不支持多重继承,却可以支持实现多个接口,因此,我们有时可以给一些继承了某些父类的类,通过实现 Runnable 的方法增加线程功能。

    1.3并发和并行

    并发是指两个或多个事件在同一时间间隔内发生。操作系统的并发性是指计算机系统中同时存在多个运行的程序,因此它具有处理和调度多个程序同时执行的能力。在操作系统的目的是使程序能并发执行。

    并发是指同一时间间隔,并行是指同一时刻。在多线程环境下,一段时间内宏观上有多个线程在同时执行,而在每个时刻,单处理机环境下实际仅能有一道程序执行,因此微观上这些线程仍是分时交替执行的。操作系统的并发性是通过分时得以实现的。并行性是指系统具有同时进行运算或操作的特性,在同一时刻能完成两种或两种以上的工作。并行需要相关硬件支持,多个CPU

    2.线程运行的内存模型和硬件架构

    2.1线程运行的内存架构

    每一个运行在Java虚拟机里的线程都拥有自己的线程栈。这个线程栈包含了这个线程调用的方法当前执行点相关的信息。一个线程仅能访问自己的线程栈。一个线程创建的本地变量对其它线程不可见,仅自己可见。即使两个线程执行同样的代码,这两个线程任然在在自己的线程栈中的代码来创建本地变量。因此,每个线程拥有每个本地变量的独有版本。

    所有原始类型的本地变量都存放在线程栈上,因此对其它线程不可见。一个线程可能向另一个线程传递一个原始类型变量的拷贝,但是它不能共享这个原始类型变量自身。

    堆上包含在Java程序中创建的所有对象,无论是哪一个对象创建的。这包括原始类型的对象版本。如果一个对象被创建然后赋值给一个局部变量,或者用来作为另一个对象的成员变量,这个对象任然是存放在堆上。

    一个本地变量可能是原始类型,在这种情况下,它总是“呆在”线程栈上。一个本地变量也可能是指向一个对象的一个引用。在这种情况下,引用(这个本地变量)存放在线程栈上,但是对象本身存放在堆上。一个对象可能包含方法,这些方法可能包含本地变量。这些本地变量任然存放在线程栈上,即使这些方法所属的对象存放在堆上。一个对象的成员变量可能随着这个对象自身存放在堆上。不管这个成员变量是原始类型还是引用类型。静态成员变量跟随着类定义一起也存放在堆上。

    存放在堆上的对象可以被所有持有对这个对象引用的线程访问。当一个线程可以访问一个对象时,它也可以访问这个对象的成员变量。如果两个线程同时调用同一个对象上的同一个方法,它们将会都访问这个对象的成员变量,但是每一个线程都拥有这个本地变量的私有拷贝。

    下图演示了调用栈和本地变量存放在线程栈上,对象存放在堆上。
    在这里插入图片描述
    两个线程拥有一些列的本地变量。其中一个本地变量(本地变量2)执行堆上的一个共享对象(对象3)。这两个线程分别拥有同一个对象的不同引用。这些引用都是本地变量,因此存放在各自线程的线程栈上。这两个不同的引用指向堆上同一个对象。

    注意,这个共享对象(对象3)持有对象2和对象4一个引用作为其成员变量(如图中对象3指向对象2和对象4的箭头)。通过在对象3中这些成员变量引用,这两个线程就可以访问对象2和对象4。

    这张图也展示了指向堆上两个不同对象的一个本地变量。在这种情况下,指向两个不同对象的引用不是同一个对象。理论上,两个线程都可以访问对象1和对象5,如果两个线程都拥有两个对象的引用。但是在上图中,每一个线程仅有一个引用指向两个对象其中之一。

    2.2硬件内存架构
    下图是硬件架构的简单图示:
    在这里插入图片描述
    每个CPU都包含一系列的寄存器,它们是CPU内内存的基础。CPU在寄存器上执行操作的速度远大于在主存上执行的速度。这是因为CPU访问寄存器的速度远大于主存。

    每个CPU可能还有一个CPU缓存层。实际上,绝大多数的现代CPU都有一定大小的缓存层。CPU访问缓存层的速度快于访问主存的速度,但通常比访问内部寄存器的速度还要慢一点。一些CPU还有多层缓存,但这些对理解Java内存模型如何和内存交互不是那么重要。只要知道CPU中可以有一个缓存层就可以了。

    一个计算机还包含一个主存。所有的CPU都可以访问主存。主存通常比CPU中的缓存大得多。

    3.锁

    3.1为什么需要锁

    这涉及到了线程同步(synchronize)问题。我们通过一个例子来了解一下这个现象。

    有若干张飞机票,2 个线程去卖它们,要求没有票时能够提示: 没有票了。以最后剩下 3 张票为例。

    class TicketRunnable implements Runnable{ 
          private int ticketNum = 3;	//以 3 张票为例
          public void run(){
                 while(true){
                       String tName = Thread.currentThread().getName();
                       if(ticketNum<=0){
                               System.out.println(tName + “无票”);
                               break;
                            }else{
                                try{
                                    Thread.sleep(1000);//程序休眠 1000 毫秒
                                   }catch(Exception ex){} 
                  ticketNum--;
                  System.out.println(tName + “卖出一张票,还剩” + ticketNum + “张票”);
                         }
               }
          }
     }
     
    public class ThreadSynTest2 {
              public static void main(String[] args){ 
              TicketRunnable tr = new TicketRunnable(); 
              Thread th1 = new Thread(tr,” 线 程 1); 
              Thread th2 = new Thread(tr,” 线 程 2); th1.start();
              th2.start();
          }
     }
    

    运行结果如下:
    在这里插入图片描述
    一张票被卖出两次,甚至出现负数。显然结果不可靠。这时候我们就需要锁。

    3.2如何解决上述问题

    怎样解决这个问题?很简单,就是让一个线程卖票时其他线程不能抢占 CPU。根据定义,实际上相当于要实现线程的同步,通俗地讲,可以给共享资源(在本例中为票)加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。
    有一种比较直观的方法,可以在共享资源(如“票”)每一个对象内部都增加一个新成员,标识“票”是否正在被卖中,其他线程访问时,必须检查这个标识,如果这个标识确定票正在被卖中,线程不能抢占 CPU。这种设计理论上当然也是可行,但由于线程同步的情况并不是很普遍,仅仅为了这种小概率事件,在所有对象内部都开辟另一个成员空间,带来极大的空间浪费,增加了编程难度,所以,一般不采用这种方法。现代的编程语言的设计思路都是把同步标识加在代码段上,确切的说,是把同步标识放在“访问共享资源(如卖票)的代码段”上。
    在 Java 语言中,synchronized 关键字可以解决这个问题,整个语法形式表现为:

    synchronized(同步锁对象) {
    // 访问共享资源,需要同步的代码段
     	}	
    

    注意,synchronized 后的“同步锁对象”,必须是可以被各个线程共享的,如 this、某个全局标量等。不能是一个局部变量。
    其原理为:当某一线程运行同步代码段时,在“同步锁对象”上置一标记,运行完这段代码,标记消除。其他线程要想抢占 CPU 运行这段代码,必须在“同步锁对象” 上先检查该标记,只有标记处于消除状态,才能抢占 CPU。在上面的例子中,this 是一个“同步锁对象”。
    因此,在上面的案例中,可以将将卖票的代码用 synchronized 代码块包围起来,
    “同步锁对象”取 this。代码如下:

    class TicketRunnable implements Runnable { 
          private int ticketNum = 3; // 以 3 张票为例
          public void run() {
              while (true) {
                  String tName = Thread.currentThread().getName();
                 // 将需要独占 CPU 的代码用 synchronized(this)包围起来
                  synchronized (this) {
                     if (ticketNum <= 0) { 
                       System.out.println(tName + “票”); break;
                     } else {
                           try {
                               Thread.sleep(1000);// 程序休眠 1000 毫秒
                          } catch (Exception ex) {}
                   ticketNum--; 
                   System.out.println(tName + “卖出一张票,还剩” +ticketNum “张票”);
                   }
                }
            }
          }
    }
    
    public class ThreadSynTest3 {
        public static void main(String[] args) { 
            TicketRunnable tr = new TicketRunnable(); 
            Thread th1 = new Thread(tr, “ 线 程 1);
            Thread th2 = new Thread(tr, “线程 2);
            th1.start();
            th2.start();
        }
    }
    

    运行结果如下:
    在这里插入图片描述
    从以上代码可以看出,该方法的本质是将需要独占CPU 的代码用**synchronized(this)**包围起来。如前所述,一个线程进入这段代码之后,就在 this 上加了一个标记,直到该线程将这段代码运行完毕,才释放这个标记。如果其他线程想要抢占 CPU,先要检查 this 上是否有这个标记。若有,就必须等待。
    实际上,在 Java 内,还可以直接把 synchronized 关键字直接加在函数的定义上, 这也是一种可以推荐的方法。如:

    public synchronized void f1() {
    // f1 代码段
    }
    

    效果等价于:

    public void f1()	{ 
        synchronized(this){
    // f1 代码段
        }
    }
    

    3.3Lock类

    我们也可以使用Java中的Lock类,使用方法如下:

    public void number(){
    		//添加锁
    		lock.lock();
    		//原子操作   volatile
    		data.count++;
    		System.out.println(Thread.currentThread().getName()+": "+data.count);
    	
    		//释放锁
    		lock.unlock();
    	}
    
    

    将需要独占资源的代码写在lock方法与unlock方法之间。
    原子操作:原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会切换到其他线程。

    展开全文
  • 多进程与多线程 多进程多进程可实现同一时间点多个任务并行。如:电脑同时运行多个程序,比如同时运行qq和微信,qq和微信就是两个不同的进程 多线程: 一个应用程序中有多个执行部分可同时执行,同一时间完成多项...
  • 主要理解什么是多线程多进程 协程 同步异步 并发并行,以及他们之间的关系和应用 一、多线程多进程 协程 上图就反应了多进程多线程,协程之间的关系。多个进程运行在不同的CPU就是并行,运行在相同的CPU就是并发...
  • 一、使用 CountDownLatch 模拟多线程并发(并行) CountDownLatch 介绍: countDownLatch是在java1.5被引入,跟它一起被引入的工具类还有CyclicBarrier、Semaphore、concurrentHashMap和BlockingQueue。 存在于java...
  • 任务CPU承担了所有的计算任务。一个CPU在一个时间切片里只能运行一个程序。当我们想同时运行多于一个程序的时候,就是任务,例如同时运行微信,QQ,浏览器等等。任务的目的是提升程序的...并发当任务数≥ C...
  • 进程:在任务操作系统中,应用程序被加载到内存运行、并分配一定的运行空间。我们把这一过程称之为进程。即进程可说是加载进内存并分配了一定空间的程序。当然一个应用程序可能不止一个进程构成,但至
  • 多线程用于实现并发并发不是真正意义上的“同时进行”,只是CPU把一个时间段划分成几个时间片段(时间区间),然后在这几个时间区间之间来回切换,由于CPU处理的速度非常快,只要时间间隔处理得当,即可让用户感觉是多...
  • 任务的目的:提升程序的执行效率,更充分利用cpu的资源并行:当任务数小于或者等于cpu核数时,每一个任务都有对应的cpu来处理执行,即任务真的是一起执行的并发:当任务数多于cpu核数,通过操作系统的各种任务调度...
  • 进程与线程1.定义进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.线程进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本...
  • 主要内容:认识多任务、多进程、单线程、多线程要认识多线程就要从操作系统的原理说起。以前古老的DOS操作系统(V 6.22)是单任务的,还没有线程的概念,系统在每次只能做一件事情。比如你在copy东西的时候不能rename...
  • 1.使用concurrent.futures可通过concurrent.futures中的ThreadPoolExecutor、 ProcessPoolExecutor分别实现多线程多进程并行任务。以具体例子来说话:假设程序用来获取一个数列中各元素是否为素数。PRIMES = ...
  • 1.服务器处理并发的必要性 如下图所示, 当一个客户端服务器建立...这个问题可以通过引入多线程多进程来解决。服务端接受一个客户端的连接后,创建一个线程或者进程,然后在新创建的线程或进程中循环处理数据。主
  • 多线程、多任务、多进程机制与编程思想的关系一、并发1.1 并发与并行首先介绍一下并发与并行,两者虽然只有一字之差,但实际上却有着本质的区别,其概念如下:并行性(parallel):指在同一时刻,有多条指令在多个...
  • Python并发(多线程)

    2021-04-26 19:18:27
    本篇文章帮大家学习python并发(多线程),包含了Python并发(多线程)使用方法、操作技巧、实例演示和注意事项,有一定的学习价值,大家可以用来参考。并发性常常被误解为并行性。 并发意味着调度独立代码以系统方式...
  • 并行需要的核心条件 多进程实现并行问题一: 什么是进程?计算机程序是存储在磁盘上的文件。只有把它们加载到内存中,并被操作系统调用 它们才会拥有其自己的生命周期。进程表示一个正在执行的程序。每个进程都有...
  • 上一篇文章《基于Python3单进程+多线程+多协程的生产者-消费者模型示例代码》介绍了如何使用Python在单进程的情况下利用协程并发地处理数据,由于Python的GIL,所有代码只利用到了一个CPU核心,无法发挥多核心优势,...
  • shell 多线程并发执行

    2021-03-25 11:04:43
    shell 多线程并发执行 #线程数,这个进程数可以理解为在管道中占6个位置 thread_num=6 tmp_fifofile="/tmp/$.fifo"mkfifo".fifo" mkfifo ".fifo"mkfifo"{tmp_fifofile}" exec 6<>"${tmp_fifofile}" rm -f ${...
  • 进程中所包含的一个或个执行单元称为线程(thread)。进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该...
  • 1.程序、进程线程 二进制程序(binaries)是指保存在存储介质上的程序,以给定操作系统和计算机体系结构可访问的格式编译生成,可以运行但尚未开始。...一个进程包含一个或线程。如果一个进程只包含一个线程,则该
  • 多线程并发多线程多进程多线程一定快吗学习并发的四个阶段一、你真的了解并发吗?为什么要使用并发?我们有没有见过并发的场景呢?其实并发是无处不在的,比如,淘宝的双十一就是一个非常高的并发量,就是很多人...
  • Java多线程并发

    2021-07-26 13:40:43
    每个进程都有自己独立的一块内存空间,一个进程可以拥有线程,比如在windows中,一个运行的xx.exe就是一个进程 线程的概念 进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一...
  • 思考:java多线程同时写一个文件第一种情况是:一个线程A有对文件加锁,另一个线程B没对文件加锁在windows7环境下:(持有锁的可以写文件成功)。在linux centos 6.3环境下:(都可以写文件成功,表现为数据交叉写入)第...
  • 认识多任务、多进程、单线程、多线程要认识多线程就要从操作系统的原理说起。现在的操作系统都是多任务操作系统,每个运行的任务就是操作系统所做的一件事情,比如你在听歌的同时还在用MSN和好友聊天。听歌和聊天...
  • tcp多进程并发文件服务器代码?线程是相对独立的执行单位,是计算机系统进行调度的...python多进程,多线程分别是并行还是并发编写多进程/多线程并发的程序时需要注意哪些方面编写多进程/多线程并发的程序时需要注...
  • 文章目录python并发编程的三种方式:CPU密集型计算、IO密集型计算多线程多进程、多协程的对比怎么根据任务选择对应的技术 python并发编程的三种方式: 多线程Thread 多进程Process 多协程Coroutine &...
  • 今天我们来聊聊Python里面的多进程与多线程编程模式。01 多线程工作在开始讲今天的正文之前,先给大家介绍一个概念「多线程工作」,这个概念可能有的人听过,也可能有的人平常工作中就是这么做的。我再来给大家讲讲...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 325,104
精华内容 130,041
关键字:

多线程并发与多进程并发