精华内容
下载资源
问答
  • 摘要: Java通过代码模拟高并发可以以最快的方式发现我们系统中潜在的线程安全问题,此处使用Semaphore(信号量)和 CountDownLatch(闭锁)搭配ExecutorService(线程池)来进行模拟。Java通过代码模拟高并发可以...

    摘要: Java通过代码模拟高并发可以以最快的方式发现我们系统中潜在的线程安全性问题,此处使用Semaphore(信号量)和 CountDownLatch(闭锁)搭配ExecutorService(线程池)来进行模拟。

    Java通过代码模拟高并发可以以最快的方式发现我们系统中潜在的线程安全性问题,此处使用Semaphore(信号量)和 CountDownLatch(闭锁)搭配ExecutorService(线程池)来进行模拟,主要介绍如下:

    1、Semaphore

    JDK 1.5之后会提供这个类

    Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。

    2、CountDownLatch

    JDK 1.5之后会提供这个类,

    CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

    CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

    如下图:

    Java如何使用代码模拟高并发操作

    以上两个类可以搭配使用,达到模拟高并发的效果,以下使用代码的形式进行举例:

    Java如何使用代码模拟高并发操作

    如上方法模拟5000次请求,同时最大200个并发操作,观察最后的结果,发现每次的结果都有差别,和预期不符,得出结果部分如下:

    Java如何使用代码模拟高并发操作

    最后结论:add 方法 非线程安全

    那如何保证add方法 线程安全,将add方法进行如下修改即可:

    Java如何使用代码模拟高并发操作

    执行结果如下:

    Java如何使用代码模拟高并发操作

    最后结论:修改后 的 add 方法 线程安全

    展开全文
  • 了解如何借助线程和并发性提升支持 Oracle 数据库的 Python ...并发性使得编程模式发生了新的转变,可以编写异步代码,从而将多个任务分散到一组线程或进程中并行工作。如果您不是编程新手并且很熟悉 C 或 C++,您可

    了解如何借助线程和并发性提升支持 Oracle 数据库的 Python 应用程序的吞吐量和响应性。 

    作者:Yuli Vasiliev

    2009 年 4 月发布

    随着趋势发展的核心转向更多而不是更快发展,最大限度地提高并发性的重要性日益凸显。并发性使得编程模式发生了新的转变,可以编写异步代码,从而将多个任务分散到一组线程或进程中并行工作。如果您不是编程新手并且很熟悉 C 或 C++,您可能已经对线程和进程有所了解,并且知道它们之间的区别。在进行并发编程时,线程提供了进程的轻量级替代物,在大多数情况下多线程较多进程更受青睐。因此,本文将讨论如何通过多线程来实现并发性。

    与很多其他编程语言一样,在使用多 CPU 计算机时将占用大量 CPU 的任务分散到 Python 中的多个线程中(可以使用 Python 标准库中的多进程模块实现)可以提高性能。对于单处理器计算机,这样确实可以并行运行多个操作,而不是只能在任务间切换且在任何指定时间只能执行一个任务。相反,在将多线程的 Python 程序移到一个多 CPU 计算机时,由于全局解释器锁 (GIL) 的原因您不会注意到任何性能提升,Python 使用 GIL 保护内部数据结构,确保在一次只有一个线程运行 CPython 虚拟机。

    但是,您可能仍然有兴趣向支持数据库的 Python 程序中添加线程以加快其速度。关键是 Python 与之交互的底层数据库很可能安装在并行处理提交的查询的高性能服务器上。这意味着您可以从提交多个查询到数据库服务器并在单独的线程中并行进行的操作中受益,而不是在一个线程中一个接一个地按顺序发出查询。

    要注意的是:尽管利用任务自身的并行性可以显著提升应用程序性能,但是我们必须认识到,不是所有任务都可并行执行。例如,在客户请求的操作(例如转账)完成之前,您无法向客户发出确认电子邮件。很显然,此类任务必须按特定顺序执行。

    另外,构建多线程代码时还要记住,某些并行运行的线程可能同时尝试更改共享的对象,这可能导致数据丢失、数据残缺,甚至损坏正在更改的对象。要避免此问题,应该控制对共享对象的访问,使得一个线程一次只能使用一个此类对象。幸运的是,利用 Python 可以实施一个锁定机制来同步对共享对象的访问(利用线程模块中的锁定工具)。

    使用锁定的缺点是损失了可扩展性。设计可扩展性时,不要忘记,对一个线程内的某个资源进行锁定将使该资源在所有其他正在运行的线程和进程中不可用,直至该锁定被释放为止。因此,要确保高效的资源管理,不应过多地使用锁定,尽可能避免锁定,如果需要使用锁定也要尽可能早地释放该锁定。

    幸运的是,当您处理存储在 Oracle 数据库中的资源时不必担心锁定。这是因为,在并发环境中对共享数据提供访问时,Oracle 数据库将使用其自身的后台锁定机制。因此,通常较好的做法是将共享数据存储在 Oracle 数据库中,从而由 Oracle 数据库处理并发性问题。

    异步执行操作也是实现可扩展性和受益于并发性的较好方式。在异步编程中,阻塞代码排队等待稍后单独的线程完成,从而确保您的应用程序可以继续执行其他任务。使用异步框架(如 Twisted)可以极大地简化构建异步应用程序的任务。

    本文将简单介绍如何使用 Python 和 Oracle 数据库构建并发应用程序,描述如何使用 Python 代码利用线程与 Oracle 数据库交互,并解释如何将 SQL 查询并行提交到数据库服务器而不是依次处理。您还将了解如何让 Oracle 数据库处理并发性问题以及如何利用 Python 事件驱动的框架 Twisted。

    Python 中的多线程编程

    线程是并行处理中的一个非常有用的特性。如果您的一个程序正在执行耗时的操作并且可以将其分成若干个独立的任务并行执行,那么使用线程可以帮助您构建更加高效、快速的代码。多线程的另一个有趣的用处是可以提高应用程序的响应能力 — 在后台执行耗时操作的同时,主程序仍然可以做出响应。

    当长时间运行的 SQL 语句彼此并无关联并且可以并行执行时,将这些语句封装到 Python 的不同线程中是不错的做法。例如,如果 Web 页面将初始的 SQL 查询并行提交到数据库服务器而不是按顺序处理它们(使它们一个接一个地排队等待),则可显著减少 Web 页面的加载时间。

    当您需要将某些大对象 (LOB) 上载到数据库时,也会发现线程很有用。以并行方式执行此操作不仅可以减少将 LOB 上载到数据库所需的整体时间,还可以在后台进行并行上载的同时保持程序主线程的响应能力。

    假设您需要将几个二进制大对象 (BLOB) 上载到数据库并将其保存到 blob_tab 表(您可能已经在自定义数据库模式中创建了该表),如下所示:

    CREATE TABLE blob_tab(
       id NUMBER PRIMARY KEY,
       blobdoc BLOB
    );
    
    CREATE SEQUENCE blob_seq;
    

    首先,我们来了解一下如何不利用线程将 BLOB 一个接一个地存储到 blob_tab 表中。以下 Python 脚本可以完成该任务,永久保存分别使用文件名和 URL 获得的两个输入图像。该示例假设您已经在 usr/pswd 自定义数据库模式中创建了 blob_tab 表和 blob_seq 序列:

    #File: singlethread.py
    #Storing BLOBs in a single thread sequentially, one after another
    
    import cx_Oracle
    from urllib import urlopen
    
    inputs = []
    #if you?ˉre a Windows user, the path could be 'c:/temp/figure1.bmp'
    inputs.append(open('/tmp/figure1.bmp', 'rb'))
    inputs.append(urlopen('http://localhost/mypictures/figure2.bmp', 'rb'))
    #obtaining a connection and predefining a memory area for a BLOB
    dbconn = cx_Oracle.connect('usr', 'pswd', '127.0.0.1/XE')
    dbconn.autocommit = True
    cur = dbconn.cursor()
    cur.setinputsizes(blobdoc=cx_Oracle.BLOB)
    #executing INSERT statements saving BLOBs to the database
    for input in inputs:
       blobdoc = input.read()
       cur.execute("INSERT INTO blob_tab (ID, BLOBDOC) VALUES(blob_seq.NEXTVAL, :blobdoc)", {'blobdoc':blobdoc})
       input.close()
    dbconn.close()
    

    尽管获取和存储 figure1.bmp 和 figure2.bmp 的任务在此处一个接一个地进行,但是,您可能已经猜到,这些任务实际上并不存在顺序上的先后关联性。因此,您可以重构上述代码,使其在单个线程中读取和存储每个图像,从而通过并行处理提升性能。在这种特殊的情况下值得一提的是,您不必协调并行运行的线程,从而可以极大地简化编码。

    以下示例显示了如何利用面向对象的方法重新编写上述脚本以使用线程。具体来说,该示例说明了如何从 threading 模块扩展 Thread 类,针对特定任务对其进行自定义。

    #File: multithread.py
    #Storing BLOBs in separate threads in parallel
    
    import cx_Oracle
    import threading
    from urllib import urlopen
    
    #subclass of threading.Thread
    class AsyncBlobInsert(threading.Thread):
      def __init__(self, cur, input):
        threading.Thread.__init__(self)
        self.cur = cur
        self.input = input
      def run(self):
        blobdoc = self.input.read()
        self.cur.execute("INSERT INTO blob_tab (ID, BLOBDOC) VALUES(blob_seq.NEXTVAL, :blobdoc)", {'blobdoc':blobdoc})
        self.input.close()
        self.cur.close()
    #main thread starts here
    inputs = []
    inputs.append(open('/tmp/figure1.bmp', 'rb'))
    inputs.append(urlopen('http://localhost/_figure2.bmp', 'rb'))
    dbconn = cx_Oracle.connect('usr', 'pswd', '127.0.0.1/XE',threaded=True)
    dbconn.autocommit = True
    for input in inputs:
       cur = dbconn.cursor()
       cur.setinputsizes(blobdoc=cx_Oracle.BLOB)
       th = AsyncBlobInsert(cur, input)
       th.start()
    

    在上述代码中,注意 threaded 属性的使用,该属性作为参数传递到 cx_Oracle.connect 方法。通过将其设置为 true,您指示 Oracle 数据库使用 OCI_THREADED 模式(又称为 threaded 模式),从而指明应用程序正在多线程环境中运行。请注意,在此处针对单线程应用程序使用 threaded 模式并不是一种好的做法。根据 cx_Oracle 文档,在单线程应用程序中将 threaded 参数设置为 true 将使性能下降 10% 到 15%。

    在本示例中,您将在两个线程间共享一个连接,但是将为每个线程创建一个单独的游标对象。此处,读取 BLOB 然后将其插入数据库的操作是在 threading.Thread 标准 Python 类中 AsyncBlobInsert 自定义子类的改写的 run 方法中实现的。因此,要在单独的线程中开始上载 BLOB,您只需创建一个 AsyncBlobInsert 实例,然后调用其 start 方法。

    这里要讨论一个与脚本有关的问题。执行时,它不会等到正在启动的线程完成 — 启动子线程后主线程将结束,不会等到子线程完成。如果您并不希望这样而是希望程序仅在所有线程都完成后再结束,那么您可以在脚本末尾调用每个 AsyncBlobInsert 实例的 join 方法。这将阻塞主线程,使其等待子线程的完成。对前面的脚本进行修改,使其等待 for 循环中启动的所有线程完成,如下所示:

    ...
    th = []
    for i, input in enumerate(inputs):
       cur = dbconn.cursor()
       cur.setinputsizes(blobdoc=cx_Oracle.BLOB)
       th.append(AsyncBlobInsert(cur, input))
       th[i].start()
    #main thread waits until all child threads are done
    for t in th:
       t.join()
    

    下一节中提供了需要强制主线程等待子线程完成的示例。

    同步对共享资源的访问

    前面的示例显示了一个多线程的 Python 应用程序,该程序处理几个彼此并无关联的任务,因此很容易分离并放到不同的线程中进行并行处理。但是在实际中,您经常需要处理彼此相互关联的操作,并且需要在某个时刻进行同步。

    作为单个进程的一部分,线程共享相同的全局内存,因此可以通过共享资源(如变量、类实例、流和文件)在彼此之间传递信息。但是,这种在线程间交换信息的简单方法是有条件的 — 当修改的对象可以同时在另一线程中访问和/或修改时,您确实要非常谨慎。因此,如果能够避免冲突,使用一个机制来同步对共享数据的访问,这将是很有用的。

    为帮助解决这一问题,Python 允许您指定锁定,然后可以由某个线程取得该锁定以确保对该线程中您所使用的数据结构进行独占访问。Threading 模块附带有 Lock 方法,您可以使用该方法指定锁定。但是请注意,使用 threading.Lock 方法指定的锁定最初处于未锁定状态。要锁定一个分配的锁,您需要显式调用该锁定对象的 acquire 方法。之后,可以对需要锁定的对象执行操作。例如,当向线程中的 stdout 标准输出流进行写入时,您可能需要使用锁,以免其他使用 stdout 的线程发生重叠。进行此操作后,您需要使用锁定对象的 release 方法释放该锁,以使释放的数据结构可用于其他线程中的进一步处理。

    关于锁定要注意的是,它们并不绑定到单个线程。在一个线程中指定的锁,可以由另一个线程获得,并由第三个线程释放。以下脚本例举了实际操作中的一个简单的锁。此处,为在子线程中进行使用,您在主线程中指定了一个锁,在向 DOM 文档写入之前获得它,然后立即释放。

    #File: synchmultithread.py
    #Using locks for synchronization in a multithreaded script
    
    import sys
    import cx_Oracle
    import threading
    from xml.dom.minidom import parseString
    from urllib import urlopen
    
    #subclass of threading.Thread
    class SynchThread(threading.Thread):
       def __init__(self, cur, query, dom):
         threading.Thread.__init__(self)
         self.cur = cur
         self.query = query[1]
         self.tag = query[0]
         self.dom = dom
       def run(self):
         self.cur.execute(self.query)
         rslt = self.cur.fetchone()[0]
         self.cur.close()
         mutex.acquire()
         sal = self.dom.getElementsByTagName('salary')[0]
         newtag = self.dom.createElement(self.tag)
         newtext = self.dom.createTextNode('%s'%rslt)
         newtag.appendChild(newtext)
         sal.appendChild(newtag)
         mutex.release()
    #main thread starts here
    domdoc = parseString('<employees><salary/></employees>')
    dbconn = cx_Oracle.connect('hr', 'hr', '127.0.0.1/XE',threaded=True)
    mutex = threading.Lock()
    queries = {}
    queries['avg'] = "SELECT AVG(salary) FROM employees"
    queries['max'] = "SELECT MAX(salary) FROM employees"
    th = []
    for i, query in enumerate(queries.items()):
       cur = dbconn.cursor()
       th.append(SynchThread(cur, query, domdoc))
       th[i].start()
    #forcing the main thread to wait until all child threads are done
    for t in th:
       t.join()
    #printing out the result xml document
    domdoc.writexml(sys.stdout)
    
    

    在上面的脚本中,您首先在主线程中创建了一个文档对象模型 (DOM) 文档对象,然后在并行运行的子线程中修改该文档,添加包含从数据库获取的信息的标签。此处,您将针对 HR 演示模式中的 employees 表使用了两个简单的查询。为避免在向 DOM 对象并行写入期间发生冲突,您需要在每个子线程中获取在主线程中指定的锁。一个子线程获得该锁后,另一个子线程将无法修改此处处理的 DOM 对象,直至第一个线程释放该锁。

    然后,您可以使用主线程同步在各子线程中对 DOM 对象所做的更新,在主线程中调用每个子线程对象的 join 方法。之后,您可以在主流中对 DOM 文档对象进行进一步处理。在该特定示例中,您只是将其写入 stdout 标准输出流。

    因此,您可能已经注意到,此处展示的示例实际上并没有讨论如何锁定数据库访问操作,例如,发出查询或针对并行线程中的同一数据库表进行更新。实际上,Oracle 数据库有自己的强大锁定机制,可确保并发环境中的数据完整性。而您的任务是正确使用这些机制。下一节中,我们将讨论如何利用 Oracle 数据库特性控制对共享数据的并发访问,从而让数据库处理并发性问题。

    使 Oracle 数据库管理并发性

    如上所述,当对存储在 Oracle 数据库中的共享数据进行访问或操作时,您不必在 Python 代码中手动实施资源锁定。为解决并发性问题,Oracle 数据库根据事务概念在后台使用不同类型的锁和多版本并发性控制系统。在实际操作中,这意味着,您只需考虑如何正确利用事务以确保正确访问、更新或更改数据库数据。具体来说,您必须谨慎地在自动提交事务模式和手动提交事务模式之间做出选择,将多个 SQL 语句组合到一个事务中时也需小心仔细。最后,必须避免发生并发事务间的破坏性交互。

    在这里,需要记住的是,您在 Python 代码中使用的事务与连接而非游标相关联,这意味着您可以轻松地按照逻辑将使用不同游标但通过相同连接执行的语句组合到一个事务中。但是,如果您希望实施两个并发事务,则需要创建两个不同的连接对象。

    在前面的“Python 中的多线程编程”一节中讨论的多线程示例中,您将连接对象的 autocommit 模式设置为 true,从而指示 cx_Oracle 模块在每个 INSERT 语句后隐式执行 COMMIT。在这种特定情况下,使用自动提交模式是合理的,因为这样可以避免子线程和主线程间的同步,从而可以在主线程中手动执行 COMMIT,如下所示:

    ...
    #main thread waits until all child threads are done
    for t in th:
       t.join()
    #and then issues a commit
    dbconn.commit()
    

    但是,在有些情况下,您需要用到上述方案。考虑以下示例。假设您在两个并行线程中分别执行以下两个操作。在一个线程中,您将采购订单文档保存到数据库中,包括订单详细信息。在另一个线程中,您对包含该订单中涉及产品的相关信息的表进行修改,更新可供购买的产品数量。

    很显然,上述两个操作必须封装到一个事务中。为此,您必须关闭 autocommit 模式,该模式为默认模式。此外,您还将需要使用主线程同步并行线程,然后显式执行 COMMIT,如上述代码段所示。

    虽然上述方案可以轻松实现,但在实际中,您可能最希望在数据库中实施第二个操作,即更新可供购买的产品的数量,将 BEFORE INSERT 触发器放到存储订单详细信息的表上,这样它可以自动更新包含相关产品信息的表中的相应记录。这将简化 Python 端的代码并消除编写多线程 Python 脚本的需求,让 Oracle 数据库来处理数据完整性问题。实际上,如果在放入 details 表的 BEFORE INSERT 触发器中更新产品表时出现问题,Oracle 数据库将自动回滚将新行插入到 details 表的操作。在 Python 端,需要进行的操作仅是将用于保存订单详细信息的所有 INSERT 封装到一个事务中,如下所示:

    ...
    dbconn = cx_Oracle.connect('hr', 'hr', '127.0.0.1/XE',threaded=True)
    dbconn.autocommit = False
    cur = dbconn.cursor()
    ...
    for detail in details:
       id = detail['id']
       quantity = person['quantity']
       cur.execute("INSERT INTO details(id, quantity) VALUES(:id, :quantity)", {'id':id, 'quantity':quantity})
    dbconn.commit()
    ...
    
    

    使用 Python 事件驱动的框架 Twisted

    Twisted 提供了一种不增加复杂性的编码事件驱动应用程序的好方法,使 Python 中的多线程编程更加简单、安全。Twisted 并发性模式基于无阻塞调用概念。您调用一个函数来请求某些数据并指定一个在请求数据就绪时调用的回调函数。而于此同时,程序可以继续执行其他任务。

    twisted.enterprise.adbapi 模块是一个异步封装程序,可用于任何 DB-API 兼容的 Python 模块,使您可以以无阻塞模式执行数据库相关任务。例如,使用它,您的应用程序不必等待数据的连接建立或查询完成,而是并行执行其他任务。本节将介绍几个与 Oracle 数据库交互的 Twisted 应用程序的简单示例。

    Twisted 不随 Python 提供,需要下载并在装有 Python 的系统中安装。您可以从 Twisted Matrix Labs Web 站点 http://twistedmatrix.com 下载适合您 Python 版本的 Twisted 安装程序包。下载程序包之后,只需在 Twisted 设置向导中进行几次点击即可完成安装,安装大约需要一分钟的时间。

    Twisted 是一个事件驱动的框架,因此,其事件循环一旦启动即持续运行,直到事件完成。在 Twisted 中,事件循环使用名为 reactor 的对象进行实施。使用 reactor.run 方法启动 Twisted 事件循环,使用 reactor.stop 停止该循环。而另一个名为 Deferred 的 Twisted 对象用于管理回调。以下是简化了的现实中的 Twisted 事件循环和回调示例。__name__ 测试用于确保解决方案将仅在该模块作为主脚本调用但不导入时(即,必须从命令行、使用 IDLE Python GUI 或通过单击图标调用该解决方案)运行。

    #File: twistedsimple.py
    #A simple example of a Twisted app
    
    from twisted.internet import reactor
    from twisted.enterprise import adbapi
    
    def printResult(rslt):
       print rslt[0][0]
       reactor.stop()
    
    if __name__ == "__main__":
       dbpool = adbapi.ConnectionPool('cx_Oracle', user='hr', password ='hr', dsn='127.0.0.1/XE')
       empno = 100
       deferred = dbpool.runQuery("SELECT last_name FROM employees WHERE employee_id = :empno", {'empno':empno})
       deferred.addCallback(printResult)
       reactor.run()
    

    请注意,twisted.enterprise.adbapi 模块基于标准 DB-API 接口构建,并在后台使用您在调用 adbapi.ConnectionPool 方法时指定的 Python 数据库模块。甚至您在指定 adbapi.ConnectionPool 输入参数时可以使用的一组关键字也取决于您使用的数据库模块类型。

    尽管与不同的 Python 数据库模块结合使用时语法上有一些不同,但是通过 twisted.enterprise.adbapi,您可以编写异步代码,从而可以在后台安全处理数据库相关任务的同时,继续执行您的程序流。以下示例展示了一个以异步方式查询数据库的简单 Twisted Web 应用程序。该示例假设您已经创建了 blob_tab 表并为其填充了数据(如本文开始部分“Python 中的多线程编程”一节中所述)。

    #File: twistedTCPServer.py
    #Querying database asynchronously with Twisted
    
    from twisted.web import resource, server
    from twisted.internet import reactor
    from twisted.enterprise import adbapi
    
    class BlobLoads(resource.Resource):
        def __init__(self, dbconn):
            self.dbconn = dbconn
            resource.Resource.__init__(self)
        def _getBlobs(self, txn, query):
            txn.execute(query)
            return txn.fetchall()
        def render_GET(self, request):
            query = "select id, blobdoc from blob_tab"
            self.dbconn.runInteraction(self._getBlobs, query).addCallback(
                self._writeBlobs, request).addErrback(
                self._exception, request)
            return server.NOT_DONE_YET
        def _writeBlobs(self, results, request):
            request.write("""
            <html>
            <head><title>BLOBs manipulating</title></head>
            <body>
              <h2>Writing BLOBs from the database to your disk</h2>
             """)
            for id, blobdoc in results:
              request.write("<i>/tmp/picture%s.bmp</i><br/>" % id)
              blob = blobdoc.read()
              output = open("/tmp/picture%s.bmp" % id, 'wb')
              output.write(blob)
              output.close()
       
            request.write("""
            <p>Operation completed</p>
            </body>
            </html>
            """)
            request.finish( )
        def _exception(self, error, request):
            request.write("Error obtaining BLOBs: %s" % error.getErrorMessage())
            request.write("""
            <p>Could not complete operation</p>
            </body>
            </html>
            """)
            request.finish( )
    
    class SiteResource(resource.Resource):
        def __init__(self, dbconn):
            resource.Resource.__init__(self)
            self.putChild('', BlobLoads(dbconn))
    
    if __name__ == "__main__":
        dbconn = adbapi.ConnectionPool('cx_Oracle', user='usr', password ='pswd', dsn='127.0.0.1/XE')
        site = server.Site(SiteResource(dbconn))
        print "Listening on port 8000"
        reactor.listenTCP(8000, site)
        reactor.run()
    
    

    执行时,该脚本在端口 8000 启动 TCP 服务器监听。接受客户端连接后,该脚本将下载 blob_tab 数据库中存储的所有图像,并将其作为单独的文件存储在 /tmp 文件夹中,然后将相应的消息发送回客户端。要测试应用程序,您需要运行脚本,然后将浏览器指向http://localhost:8000

    关于上述代码最应注意的是,它在继续执行程序流的前提下,以无阻塞模式运行针对数据库发出的查询。要确保它以此方式工作,可以在对 runInteraction 的调用(runInteraction 指示 Twisted 依次对 _getBlobs 和 _writeBlobs 进行异步调用)下插入一些代码以增强 render_GET 方法。新插入的代码应使用 request.write 方法将一些内容发送回客户端,这样您可以看到,该输出出现在客户端浏览器的 _writeBlobs 中生成该输出之前。

    结论

    当下,并发性在数据密集型应用程序中频繁使用。高效使用并发性是提升应用程序性能的关键所在。编写并发应用程序最高效的一种方法是使用多线程。但是,正如您在本文中所了解到的,由于全局解释器锁 (GIL) 的原因,Python 中的多线程化对多处理器计算机没有任何好处 (GIL)。但是,当将其用于开发数据库密集型代码以及异步、事件驱动的代码时,您仍然可以受益于多线程。

    本文是并发性之路的良好起点,为您提供了有价值的背景信息,有助于决策如何充分利用并发性来设计支持 Oracle 数据库的 Python 应用程序。


    展开全文
  • Java使用代码模拟高并发操作

    千次阅读 2018-10-17 16:18:34
    摘要:在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了解决多个线程并发同一资源造成的数据不一致的...

    摘要:在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了解决多个线程并发同一资源造成的数据不一致的问题。在另外一种场景下,一个资源有多个副本可供同时使用,比如打印机房有多个打印机、厕所有多个坑可供同时使用,这种情况下,Java提供了另外的并发访问控制--资源的多副本的并发访问控制,今天使用的Semaphore即是其中的一种。

    Java通过代码模拟高并发可以以最快的方式发现我们系统中潜在的线程安全性问题,此处使用Semaphore(信号量)和CountDownLatch(闭锁)搭配ExecutorService(线程池)来进行模拟,主要介绍如下:

     

    1、Semaphore

    JDK 1.5之后会提供这个类

    Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。

     

    2、CountDownLatch

    JDK 1.5之后会提供这个类,

    CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

    CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

    如下图:

    以上两个类可以搭配使用,达到模拟高并发的效果,以下使用代码的形式进行举例:

     

    packagemodules;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Semaphore;publicclassCountExample{//请求总数

    publicstaticintclientTotal=5000;//同时并发执行的线程数

    publicstaticintthreadTotal=200;publicstaticintcount=0;publicstaticvoidmain(String[]args)throwsException{

    ExecutorServiceexecutorService=Executors.newCachedThreadPool();//信号量,此处用于控制并发的线程数

    finalSemaphoresemaphore=newSemaphore(threadTotal);//闭锁,可实现计数器递减

    finalCountDownLatchcountDownLatch=newCountDownLatch(clientTotal);for(inti=0;iclientTotal;i++){

    executorService.execute(()-{try{ //执行此方法用于获取执行许可,当总计未释放的许可数不超过200时,

    //允许通行,否则线程阻塞等待,直到获取到许可。

    semaphore.acquire();

    add();//释放许可

    semaphore.release();

    }catch(Exceptione){//log.error(exception,e);

    e.printStackTrace();

    }//闭锁减一

    countDownLatch.countDown();

    });

    }

    countDownLatch.await();//线程阻塞,直到闭锁值为0时,阻塞才释放,继续往下执行

    executorService.shutdown();

    log.info(count:{},count);

    }privatestaticvoidadd(){

    count++;

    }

    }

    如上方法模拟5000次请求,同时最大200个并发操作,观察最后的结果,发现每次的结果都有差别,和预期不符,得出结果部分如下:

    22:18:26.449[main]INFOmodules.CountExample-count:499722:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:499522:18:26.449[main]INFOmodules.CountExample-count:4998

    最后结论:add 方法 非线程安全

    那如何保证add方法 线程安全,将add方法进行如下修改即可:

    privatestaticvoidadd(){

    count.incrementAndGet();

    }

    执行结果如下:

    22:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:500022:18:26.449[main]INFOmodules.CountExample-count:5000

    最后结论:修改后 的 add 方法 线程安全

     

    展开全文
  • java代码优化解决高并发下的问题

    千次阅读 2018-10-27 10:49:44
    还有就是从最基础的地方优化我们写代码减少不必要的资源浪费:( 1.不要频繁的new对象,对于在整个应用中只需要存在一个实例的类使用单例模式.对于String的连接操作,使用StringBuffer或者StringBuilder.对于utility...

    一种是使用缓存、另一种是使用生成静态页面;还有就是从最基础的地方优化我们写代码减少不必要的资源浪费:(

    1.不要频繁的new对象,对于在整个应用中只需要存在一个实例的类使用单例模式.对于String的连接操作,使用StringBuffer或者StringBuilder.对于utility类型的类通过静态方法来访问。

    关于new对象就不用多说了吧,new一个对象等于开辟一个新内存。

    因为String对象一旦创建是不能改变的,总的来说StringBuffer比String在效率上的优越的。比如你执行了String类的substring,replace,toUpperCase,toLowerCase,trim方法都会生成一个新的字符串,一旦你的程序对字符串有大量修改,那么在jvm的堆内存中就会生成大量的旧的临时垃圾字符串对象。String如果长度发生改变,是非常消耗内存的。
    我们先来看一些String类的基本特点:

    (1)string是不可变的是被final修饰的,带来的好处主要有两点,第一是线程安全,可以在多个线程中共享而不需要加锁,第二是由于不变性所以它的hashcode可以被缓存后提升效率,这也是为什么我们见到的大多数的HashMap的key都是使用String类型的。

    (2)通过双引号定义的字符串我们称为字符串字面量,这部分字符串会被在string pool中创建,在java里面比较一个对象相等,应该优先选择equals方法而不是==方法

    (3)对于字符串拼接的 + 号,底层其实是使用StringBuffer 或者 StringBuilder来完成的。

    而StringBuffer跟StringBuilder该如何取舍呢?
    都知道StringBuffer跟StringBuilder线程最大的特点一个安全一个不安全,以及运行速度的问题
    那StringBuffer跟StringBuilder的应用场景是什么呢?
    最简单的回答是,stringbuffer 基本没有适用场景,你应该在所有的情况下选择使用 stringbuiler,除非你真的遇到了一个需要线程安全的场景,关于线程安全,即使你真的遇到了这样的场景,很不幸的是,恐怕你仍然有 99.99…99% 的情况下没有必要选择 stringbuffer,因为 stringbuffer 的线程安全,仅仅是保证 jvm 不抛出异常顺利的往下执行而已,它可不保证逻辑正确和调用顺序正确。大多数时候,我们需要的不仅仅是线程安全,而是锁。为什么会有 stringbuffer 的存在,如果真的没有价值,为什么 jdk 会提供这个类?答案太简单了,因为最早是没有 stringbuilder 的,sun决定让 stringbuffer 是线程安全的,然后大约 10 年之后,人们终于意识到了,于是,在 jdk1.5 的时候,终于决定提供一个非线程安全的 stringbuffer 实现,并命名为 stringbuilder。顺便,javac 好像大概也是从这个版本开始,把所有用加号连接的 string 运算都隐式的改写成 stringbuilder,也就是说,从 jdk1.5 开始,用加号拼接字符串已经没有任何性能损失了。
    如诸多评论所指出的,我上面说,"用加号拼接字符串已经没有任何性能损失了"并不严谨,严格的说,如果没有循环的情况下,单行用加号拼接字符串是没有性能损失的,java 编译器会隐式的替换成 stringbuilder,但在有循环的情况下,编译器没法做到足够智能的替换,仍然会有不必要的性能损耗,因此,用循环拼接字符串的时候,还是老老实实的用 stringbuilder 吧。

    2.避免使用错误的方式,如Exception可以控制方法推出,但是Exception要保留stacktrace消耗性能,除非必要不要使用 instanceof做条件判断,尽量使用比的条件判断方式.使用JAVA中效率高的类,比如ArrayList比Vector性能好。)

    instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。 (后续还有更新)

    展开全文
  • 高并发可用知识总结

    万次阅读 2019-09-02 15:48:33
    究竟啥才是互联网架构“高并发” 一、什么是高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。 高并发相关常用的一些...
  • 高并发技术

    万次阅读 多人点赞 2019-10-21 16:45:04
    高并发技术 第一章 预备知识一 理解大数据二 网工基础知识OSI七层参考模型应用层表示层会话层传输层网络层链路层物理层功能分层总结第二章 LVS技术一 LVS介绍二 LVS调度算法LVS命令监控多个端口号管理服务集群中的...
  • JVM 并发性: Java 和 Scala 并发性基础

    千次阅读 2016-03-17 11:28:30
    Java 并发性支持 在 Java 平台诞生之初,并发性支持就是它的一个特性,线程和同步的实现为它提供了超越其他竞争语言的优势。Scala 基于 Java 并在 JVM 上运行,能够直接访问所有 Java 运行时(包括所有并发性支持...
  • 不能使用负载均衡 我们的整个站点只有这一个高并发,而且这个告别发也只是临时的,活动完成之后就没了,那么我们仅仅为了这个活动去加服务器,去做负责均衡是不合适的,价比太低了,只能代码解决了、一 ,减少...
  • 高并发一致解决方案

    千次阅读 2017-11-19 20:56:10
    高并发场景有抢红包,双十一抢商品等。如何去处理这些高并发场景呢? 1.从存储介质考虑:有内存缓存和磁盘缓存,内存缓存的速度是比磁盘缓存要出几十倍的,因此可以考虑存储介质在内存上。想象一下如果抢红包的...
  • 提高系统并发性的方法总结

    千次阅读 2017-12-12 17:42:50
    提高系统并发性的方法总结
  • java高并发下的唯一验证

    千次阅读 2014-11-21 14:03:36
    做java ee程序基本上都会遇到唯一性的问题,我们通常不考虑并发性的问题的情况下的做法是:先根据条件去数据中查询是否存在,如果存在则提示不唯一,否则插入   下面是一个简单的例子, 向表t_test_curr插入数据...
  • Java多线程高并发代码示例(一)

    千次阅读 2019-02-26 17:38:42
    对某个对象加锁 /** * synchronized关键字 * 对某个对象加锁 */ public class T { private int count = 10; private Object o = new Object();... //任何线程要执行下面代码,必须先拿到o锁 synchronized ...
  • 数据一致性和并发性

    千次阅读 2014-04-26 10:41:13
    u 多用户环境中的数据并发性和一致性介绍 u Oracle如何管理数据并发性和一致性 u Oracle如何锁定数据 u Oracle闪回查询概述 多用户环境中数据并发性和一致性介绍 在单用户数据库中,用户修改数据库中的...
  • 高并发解决方案之一 ——负载均衡

    万次阅读 多人点赞 2018-04-15 21:52:15
    由于Nginx 超越 Apache 的性能和稳定,使得国内使用 Nginx 作为 Web 服务器的网站也越来越多,其中包括新浪博客、新浪播客、网易新闻、腾讯网、搜狐博客等门户网站频道等,在3w以上的高并发环境下,ngnix处理...
  • JavaWeb 并发编程 与 高并发解决方案

    万次阅读 多人点赞 2018-09-12 03:41:00
    在这里写写我学习到和自己所理解的 Java高并发编程和高并发解决方案。现在在各大互联网公司中,随着日益增长的互联网服务需求,高并发处理已经是一个非常常见的问题,在这篇文章里面我们重点讨论两个方面的问题,一...
  • 高并发处理方案

    千次阅读 2018-02-17 19:07:18
    高并发和大流量:并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。但是我们所说的并发是:...
  • 并发编程篇:java 高并发面试题

    万次阅读 多人点赞 2018-02-28 21:43:18
    文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。 一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,...
  • 写【高并发专题】有一段时间了,一些读者朋友留言说,并发编程很难,学习了很多的知识,但是在实际工作中却无从下手。对于一个线上产生的并发问题,又不知产生这个问题的原因究竟是什么。对于并发编程,感觉上似乎是...
  • redis高并发秒杀测试

    千次阅读 2018-11-08 09:06:52
    ... 准备 使用docker-compose命令启动redis服务器(可以用其他方式启动) idea启动测试项目 ... 高并发秒杀-有事务方式减少库存.jmx 重现秒杀时出现的超卖问题 核心测试代码如下: /** *...
  • 性能高并发 面试

    千次阅读 2015-04-14 21:58:26
    redis,nginx/Tengine,keeplive,DRBD,heartbeat这些小工具还是可以...至于大业务场景,除了进大公司没有别的办法,因为有些工具运行的配置要求太,必须多台服务器配合才能完成. 如果有精力,业内很喜欢用perl,python
  • redis 的incr 高并发 原子计数器

    千次阅读 2020-07-10 15:18:57
    前言:6月底 公司录单的人比较多,由于先前的系统用的同步锁 ,我们是多服务实例,导致出现重复单号的问题,我想到的解决办法有两种...Redis命令的原子使得我们不用考虑并发问题,可以方便的利用原子自增操作 简单.
  • 高并发抢购思路

    万次阅读 2016-11-24 08:57:06
    电商的秒杀和抢购,对我们来说,都不是一个陌生的东西。然而,从技术的角度来说,这对于Web系统是一个巨大的考验。当一个Web系统,在...在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整...
  • 高并发解决方案

    万次阅读 多人点赞 2015-10-12 19:31:26
    大数据量下高并发同步的讲解(不看,保证你后悔)    对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了。而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了...
  • 高并发系统的限流算法与实现

    千次阅读 多人点赞 2019-07-23 08:00:00
    开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。 缓存:缓存的目的是提升系统访问速度和增大系统处理容量。 降级:降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有...
  • java高并发详解

    千次阅读 2018-02-28 23:59:11
    对于开发的网站,如果网站的访问量非常大,那么我们应该考虑相关的、并发访问问题,并发是绝大部分程序员头疼的问题; 为了更好的理解并发和同步,先明白两个重要的概念:异步和同步; &nbsp;1、同步和异...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 546,920
精华内容 218,768
关键字:

代码的高并发性作用