资讯专栏INFORMATION COLUMN

Java并发编程笔记(一)

cnsworder / 2442人阅读

摘要:并发编程实战水平很高,然而并不是本好书。一是多线程的控制,二是并发同步的管理。最后,使用和来关闭线程池,停止其中的线程。当线程调用或等阻塞时,对这个线程调用会使线程醒来,并受到,且线程的中断标记被设置。

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

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

两个部分

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

Thread和Runnable

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

线程的几种状态:

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

Java线程池

参见Java线程池系列文章。本文略做总结。

阻塞队列 BlockingQueue

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

阻塞队列有几种实现:

ArrayBlockingQueue:基于数组结构的有界阻塞队列,按 FIFO(先进先出)原则对元素进行排序。

LinkedBlockingQueue:基于链表结构的阻塞队列,按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。

SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue。

PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

自己实现线程池

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

Java线程池

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

BlockingQueue queue = new ArrayBlockingQueue(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,且线程的中断标记被设置。如何处理这种情况取决于线程自己。具体参见这篇文章。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68538.html

相关文章

  • Java并发编程的艺术】第章读书笔记

    摘要:前言并发编程的目的是让程序跑的更快,但并不是启动更多的线程,这个程序就跑的更快。尽可能降低上下文切换的次数,有助于提高并发效率。死锁并发编程中的另一挑战是死锁,会造成系统功能不可用。 前言 并发编程的目的是让程序跑的更快,但并不是启动更多的线程,这个程序就跑的更快。有以下几种挑战。 挑战及方案 上下文切换 单核CPU上执行多线程任务,通过给每个线程分配CPU时间片的方式来实现这个机制。...

    马忠志 评论0 收藏0
  • Java并发编程笔记(二)

    摘要:本文探讨并发中的其它问题线程安全可见性活跃性等等。当闭锁到达结束状态时,门打开并允许所有线程通过。在从返回时被叫醒时,线程被放入锁池,与其他线程竞争重新获得锁。 本文探讨Java并发中的其它问题:线程安全、可见性、活跃性等等。 在行文之前,我想先推荐以下两份资料,质量很高:极客学院-Java并发编程读书笔记-《Java并发编程实战》 线程安全 《Java并发编程实战》中提到了太多的术语...

    NickZhou 评论0 收藏0
  • Java并发编程的艺术】第二章读书笔记之volatile关键字

    摘要:目前看的部分主要是这个关键字。语言提供了,保证了所有线程能看到共享变量最新的值。前缀的指令在多核处理器下会做两件事情将当前处理器缓存行的数据写回到系统内存。 这一章节的话,主要是讲一下在并发操作中常见的volatile、synchronized以及原子操作的相关知识。 目前看的部分主要是volatile这个关键字。 volatile 根据Java语言规范第3版中对volatile的定义...

    szysky 评论0 收藏0
  • 阿里 2021 版最全 Java 并发编程笔记,看完我才懂了“内卷”的真正意义

    摘要:纯分享直接上干货操作系统并发支持进程管理内存管理文件系统系统进程间通信网络通信阻塞队列数组有界队列链表无界队列优先级有限无界队列延时无界队列同步队列队列内存模型线程通信机制内存共享消息传递内存模型顺序一致性指令重排序原则内存语义线程 纯分享 , 直接上干货! 操作系统并发支持 进程管理内存管...

    不知名网友 评论0 收藏0
  • Java并发编程的艺术】第二章读书笔记之原子操作

    摘要:前言今天的笔记来了解一下原子操作以及中如何实现原子操作。概念原子本意是不能被进一步分割的最小粒子,而原子操作意为不可被中断的一个或一系列操作。处理器实现原子操作处理器会保证基本内存操作的原子性。 showImg(https://segmentfault.com/img/bVVIRA?w=1242&h=536); 前言 今天的笔记来了解一下原子操作以及Java中如何实现原子操作。 概念 ...

    olle 评论0 收藏0

发表评论

0条评论

cnsworder

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<