Java并发编程笔记(一)

《Java并发编程实战》水平很高,然而并非本好书。组织混乱、长篇大论、难以消化,中文翻译也较死板。这里是一篇批评此书的帖子,非常贴切。俗话说:“看到有这么多人骂你,我就放心了”。html

然而知识老是要学的。这里就总结一下书中及网络上的内容,做为Java并发编程之旅的结束,再也不浪费时间了。java

两个部分

这本书实际上能够分为两个部分。一是多线程的控制,二是并发同步的管理。把它们揉在一块儿,思路很难清晰。本文就先介绍第一部分,多线程的控制。编程

Thread和Runnable

在Java 5.0以前,多线程编程就是直接操做Thread。能够从Thread类派生一个类,或者实现Runnable接口的run()方法,而后调用Thread.start()启动线程。数组

线程的几种状态:网络

clipboard.png

Java 5.0增长了java.util.concurrent包,才有了线程池等强大的工具。多线程

Java线程池

参见Java线程池系列文章。本文略作总结。并发

阻塞队列 BlockingQueue

阻塞队列,顾名思义,它在基本队列的基础上,还有阻塞的功能。即,若是队列已满,则入队操做阻塞等待,直到有空位;若是队列已空,则出队操做阻塞等待,直到队列有元素。相应的方法分别为put()take()异步

阻塞队列有几种实现:异步编程

  1. ArrayBlockingQueue:基于数组结构的有界阻塞队列,按 FIFO(先进先出)原则对元素进行排序。
  2. LinkedBlockingQueue:基于链表结构的阻塞队列,按FIFO排序元素,吞吐量一般要高于ArrayBlockingQueue。
  3. SynchronousQueue:一个不存储元素的阻塞队列。每一个插入操做必须等到另外一个线程调用移除操做,不然插入操做一直处于阻塞状态,吞吐量一般要高于LinkedBlockingQueue。
  4. PriorityBlockingQueue:一个具备优先级的无限阻塞队列。

本身实现线程池

就是一个简单的生产者、消费者模型。线程池中的线程是消费者,循环地从阻塞队列中提取任务,执行任务。工具

Java线程池

Java线程池的接口是ExecutorService,它有几个实现。以ThreadPoolExecutor为例,它的使用方式是:

BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(5);  
          
ExecutorService threadPoolExecutor =  
        new ThreadPoolExecutor(  
                corePoolSize,  //初始线程数
                maxPoolSize,   //最大线程数
                keepAliveTime, //空闲线程最大存活时间 
                TimeUnit.MILLISECONDS,  //时间单位
                queue          // 任务的阻塞队列
        );

要使用这个线程池,可使用它提供的以下方法:

  • execute(Runnable) 没有返回值 。
  • submit(Runnable) 返回Future对象,表明未完成的结果(因为Runnable没有返回值因此内容为空)。
  • submit(Callable) 返回Future对象,表明未完成的结果。
  • invokeAny(Collection) 执行全部任务,返回第一个完成的结果。
  • invokeAll(Collection) 执行全部任务,返回Future对象列表。

最后,使用shutdown()shutdownNow()来关闭线程池,中止其中的线程。前者采用后文讲到的interrupt方式温和关闭,后者则调用Thread.stop()强行关闭。

Executors类

上面的线程池使用起来仍是太具体了,还须要本身建立线程池,还要本身传阻塞队列进去,很差用。因而Java提供了一个帮助类Executors,很是经常使用。

来看它的经常使用方法:

  • newFixedThreadPool(): 建立固定数量的线程池。
  • newCachedThreadPool(): 建立动态维护线程数的线程池。
  • newSingleThreadExecutor(): 建立单线程的线程池。

Callable接口和Future接口

Runnable接口的问题在于没有返回值,过于简单了。所以加入了Callable接口。相比于Runnable,一是有返回值,二是能够抛出异常。
Future就是异步编程中对一个尚未完成的任务的抽象,至关于C#中的Task。一样有cancel()isDone()等方法,调用get()则阻塞地获取结果。
FutureTaskFuture的一个具体实现类,而且不光实现了Future,还实现了Runnable接口,使其用旧方式也可调用。具体可见 Runnable、Callable、Future、FutureTask的区别

任务的取消

这是一个高级话题。Java建议不要用stop()粗暴地杀死线程,而是采用interrupt()这种温和的方式。当线程调用wait()sleep()等阻塞时,对这个线程调用interrupt()会使线程醒来,并受到InterruptedException,且线程的中断标记被设置。如何处理这种状况取决于线程本身。具体参见这篇文章

相关文章
相关标签/搜索