理解 Java 线程池 ThreadPoolExecutor

使用 JDK 5 的线程池实现有近 20 年的时间了,快速创建一个线程池经常是调用 Executors 中的工厂方法。但是涉及过更精细的线程池管理控制时不得不用 ThreadPoolExecutor 的构造方法,这也就是为什么有些公司不建议用 Executors 的工厂方法创建线程池,而应该直接创建 ThreadPoolExecutor 或 ForkJoinPool 实例。 

例如代码

ExecutorService threadPool = Executors.newFixedThreadPool(10);

实际上调用的是

new ThreadPoolExecutor(nThreads, nThreads,
                                                0L, TimeUnit.MILLISECONDS,
                                                new LinkedBlockingQueue<Runnable>());

前两个参数 corePoolSize 和 maximumPoolSize 是一样的; OL, TimeUnit.MILLISECONDS 表示线程创建后只要线程池还在就是永生的; workQueue 是一个大小为 Integer.MAX_VALUE 的队列, 几乎可以无限提交任务,耗尽内存

不建议用 Executors 的工厂方法的原因大致有二: 阅读全文 >>

Python 子进程与子进程池的应用

去年记录过一篇如何使用 Python 的线程,线程池的日志 Python 多线程编程, 需用到 threading.Thread, concurrent.futures.ThreadPoolExecutor。本文可以当作是上一文 Python 多线程编程的姊妹篇。

Python 的多线程受到 GIL(Global Interpreter Lock) 的限制,GIL 是一把加到了 Python 的解释器的锁,使得在任意时刻只允许一个 Python  进程使用 Python 解释器,也就是任意时刻,Python 只有一个线程在运行。

GIL 严重影响了计算密集型(CPU-bound) 的多线程程序,此时的多线程与单线程性能没什么差异,也发挥不了多核的威力。但对 I/O 密集型(I/O-bound) 影响不大,因为 CPU 多数时候是在等待。

为了突破 GIL 的 CPU 密集型程序的限制,可以使用非 CPython 解释器,如 Jython, IronPython 或 PyPy, 更为现实的做法就是使用子进程来替代线程去承担较为繁重的计算任务,因为 GIL 是加在进程上的,所以新的进程有独立的 GIL. 阅读全文 >>

Python 线程池使用有限大小的工作队列

在去年的一篇 Python 多线程编程 中学习了 Python 中如何使用多线程来调度任务,工作中也不时从自己的博客中找来参考。在运用当中不时的碰到内存消耗殆尽情况,直接把命令行窗口打死,不得不强行关窗口或杀进程。之前一直未意识到问题所在,只知任务太多就必死无疑,现在要用 Python 来处理大量任务了,必须着手来解决一下它。其实原因很简单,和 Java 的 ThreadPoolExecutor 一样(看它们用的类名都是一样的)。Java 的  ThreadPoolExecutor 内部使用了一个 Integer.MAX_VALUE 的 LinkedBlockingQueue 来存放提交的待处理的任务,所以基本上就是一个无底洞,自然解决办法也是类似的,需要一个 Bounded Queue 来存放任务列表。

在解决该问题之前自己也不妨来温习一下 Python 中使用线程池的基本模式,下面的模板代码曾经是我的最爱:

在上面的 with 上下文中会进行以下几步 阅读全文 >>

Java 普通线程池与 ForkJoinPool 的效果对比

Java 多线程编程常用的一个接口是 ExecutorService, 其实就一个线程池的接口,一般由两种方式创建线程池,一为 Executors 的工厂方法,二则创建 ForkJoinPool 实例,当然也有直接使用 ThreadPoolExecutor 的。

关于什么时候用 ForkJoinPool 或普通的线程池(如 Executors.newFixedThreadPool(2) 或 new ThreadPoolExecutor(...)) 不过多的述说。如果要运用到 ForkJoinTask 的话就要用 ForkJoinPool, 它是 Java7 新引入的线程池类型。

关于 Java7 的 fork-join 框架可参考很多年前的一篇 Java 的 fork-join 框架实例备忘。ForkJoinPool 的一个典型特征是能够进行 Work stealing。它也是 Akka actor 效率高效的一个有力保证。

本文只能某一种情形下在选择普通线程池与 ForkJoinPool 的区别,直接说吧,普通线程更容易造成死锁,而 ForkJoinPool 却能应对相同的状况。 阅读全文 >>

Python 多线程编程

这几天一直浸淫在对 Python 的学习当中,对于一个更习惯 Java 语言的人来说,在接接触 Python 各种概念时会不停的与 Java 进行碰撞。譬如这里要说到的线程,Python 能如何像 Java 一样创建并执行单个线程,以及是否也能使用线程池来进行多作务的执行呢?

整个读完了《THE Quick Python Book》一书也只字未提多线程,然而对于有长时间的 IO 等待的程序,对于当今普及的多核以及核内超线程的 CPU 来说,不使用多线程来并行或并发处理任务是万万不能的,否则效率的差别是数量级的。

基于与 Java 多线程编程进行的比较,主要着力于两个问题:1)创建并执行新的线程,2)线程池中执行任务

创建并执行新的线程

默认的,代码是在主线程中执行,主线程名称为 MainThread。如果要创建一个子线程并执行需要用到模块 threading。下面的是基本的代码 阅读全文 >>

并发(Concurrent) 与并行(Parallel) 的区别

刚开始阅读 《Akka IN ACTION》这本书,刚开始是对 Revolution 这个词翻译成中文是革命 感到诧异,因为革命 通俗来讲就是 杀人 的意思。至于 Revolution 英文解释不深究了,只是感叹何以颠覆性的变化就一定要杀人吗?

也由此引出了编程中经常面对的 Concurrent(名词为:Concurrency) 和 Parallel(名词为:Parallelism) 这两个词,基本上是认为它们是同一个意思。其实不然,下面慢慢道来。

如果从英文字典对它们的解释也没有多大区别,差不多都是说同是发生,但字面上 Parallel 多了一个平行的意思。所以在中文上,在计算机领域我们约定的翻译是

  • Concurrent(Concurrency)  --  并发
  • Parallel(Parallelism)           --  并行

比如在多线程环境中它们的区别具体体现在:

并发:多个任务在同一个 CPU 核上按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。针对 CPU 内核来说,任务仍然是按细粒度的串行执行。也难怪在 Java 5 中新加的并发 API 的包名是 java.uti.concurrent阅读全文 >>

使用 JDK 5 后的线程并发,Callable, Future, ExecutorServie ...

被问及 Java 多线程,多会想到 Thread, Runnable,更通常是用 new Thread(){public void run(){...}}.start() 来启动一个线程。那都是 JDK 1.5 之前的年代了,现在还这么回答就 Out 了。用用  JDK 1.5 给我们带来的  java.util.concurrent 吧,更酷了。这里不涉及它的并发集合类,同步互斥机制,只说线程及线程池的应用举例。

1. 新的启动线程的方式:

阅读全文 >>

PHP 异步执行方法,模拟多线程

PHP 本身没有多线程的东西,但可以曲线的办法来造就出同样的效果,比如多进程的方式来达到异步调用,只限于命令模式。还有一种更简单的方式,可用于 Web 程序中,那就是用 fsockopen()、fputs() 来请求一个 URL 而无需等待返回,如果你在那个被请求的页面中做些事情就相当于异步了。

关键代码如下:

上面的代码向页面 another_page.php 发送完请求就不管了,用不着等待请求页面的响应数据,利用这一点就可以在被请求的页面 another_page.php 中异步的做些事情了。 阅读全文 >>

Java 线程同步,对象锁与互斥规则

在前面一篇 是同步方法还是 synchronized 代码?-- 详解多线程同步规则 其实已清楚讲述了这里想要是解的话题,只是之前的长篇大论,没个重点,读来实在会让人受累,故在此单列一专题,假以图示,也为自己加深对同步锁的理解。道中人觉得哪里说得有出入或有更妙的理解方式,尽管拍过来。

记得我初识多线程时,就是很简单化的去理解了同步:说到要同步,无外乎就是给方法加 synchronized 关键字,或者给代码块加上 synchronized(this) 括起来[注:jdk 1.5 后可用 Lock 来同步代码],以为这样就万事大吉,就能保证一个线程执行时,另一个线程就进不来。其实不尽然,因为静态同步方法与非静态同步方法是有区别的,而 synchronized(this) 括号中是用 this 还是要用别的对象,也是有讲究的,这些问题可以阅读前一篇:是同步方法还是 synchronized 代码?-- 详解多线程同步规则阅读全文 >>

是同步方法还是 synchronized 代码?– 详解多线程同步规则

熟悉 Java 的多线程的一般都知道会有数据不一致的情况发生,比如两个线程在操作同一个类变量时,而保护数据不至于错乱的办法就是让方法同步或者代码块同步。同步时非原子操作就得同步,比如一个简单的 1.2+1 运算也该同步,以保证一个代码块或方法成为一个原子操作。

简单点说就是给在多线程环境中可能会造成数据破坏的方法,做法有两种,以及一些疑问:

1. 不论是静态的或非静态的方法都加上 synchronized 关键字,那静态的方法和非静态的方法前加上 synchronized 关键字有区别吗?

2. 或者在可疑的代码块两旁用 synchronized(this) 或 synchronized(someObject) 包裹起来,而选用 this 还是某一个对象--someObject,又有什么不同呢? 阅读全文 >>