并发编程
文章平均质量分 74
rhwayfunn
这个作者很懒,什么都没留下…
展开
-
Java并发编程系列之四:锁与volatile的内存语义
前言在前面的文章中已经提到过volatile关键字的底层实现原理:处理器的LOCK指令会使得其他处理器将缓存刷新到内存中(确切说是主存)以及会把其他处理器的缓存设置为无效。这里的内存语义则说的是在JMM中的实现,那么为什么要理解volatile和锁在JMM中的内存语义呢?主要原因是这部分内容是与程序开发息息相关的,所以在高并发量的系统中,如果对这块知识的了解欠缺的话将无法设计出优雅支持高并发的系统(原创 2015-12-02 13:47:12 · 2523 阅读 · 1 评论 -
Java并发编程系列之二十二:CyclicBarrier
CyclicBarrier意为可循环使用的(Cyclic)屏障(Barrier),属于jdk 5新增加的并发工具,需要导入java.util.concurrent.CylicBarrier才能使用。CyclicBarrier适用于这样的场景:多线程并发执行,已经执行完的线程需要阻塞等待其他线程执行完毕,最后执行主线程的工作。听起来非常类似CountDownLatch,CyclicBarrier与Co原创 2016-04-06 14:49:26 · 4942 阅读 · 0 评论 -
Java并发编程系列之二十三:Semaphore
Semaphore(信号量)是用来控制线程并发数的,如果想对特定资源限制允许访问的线程数,那么就可以使用Semaphore来协调各个线程,从而保证线程合理运行。Semaphore由一个构造方法用来指定资源允许访问的线程数,也称为许可证的数量。使用Semaphore信号量控制并发线程数很简单,只需要在构造方法中执行一个参数就可以,该参数就表示有多少个线程可以并发访问该资源。当需要一个许可证的时候,只需原创 2016-04-06 16:23:32 · 4573 阅读 · 4 评论 -
Java并发编程系列之二十四:Exchanger
Exchanger是一个线程间交换数据的工具类。Exchanger从字面上可以理解为交换者,是一个可以用于线程间协作的工具类。主要用于线程间的数据交换。Exchanger提供一个同步点,在这个同步点上,两个线程可以交换彼此的数据。这两个线程可以通过exchange方法交换数据,当然存在线程执行不同步的情况,如果第一个线程先到达同步点,那么在第二个线程到达同步点之前,第一个线程会阻塞等待,直到两个线程原创 2016-04-07 14:41:56 · 4336 阅读 · 0 评论 -
Java并发编程系列之二十五:线程池
线程池简介在之前介绍Executor框架的文章中对线程池有一个初步的认识,实际上线程池这种设计思想在Java中很普遍,比如JVM中常量池,以及Web开发使用到的数据库连接池。这些池本质上还是Java中的对象池,因为池中存放的都是Java对象。回到线程池,几乎所有需要异步或者执行并发任务的程序都可以使用到线程池。使用线程池带来的好处主要包括以下几个方面: 一,提高资源利用率。由于线程池中的线程使可原创 2016-04-07 19:50:55 · 4885 阅读 · 1 评论 -
Java并发编程系列之二十六:ConcurrentModificationException
在多线程程序的编写中,经常能遇到ConcurrentModificationException这个异常,这个异常的意思是指出现了并发修改的情况,为了降低对程序的性能的影响,Java开发者将捕获的这种错误以“善意”的方式进行提醒。这种异常一般在对容器的元素遍历的过程中出现了对容器的写操作(包括增加、修改和删除操作)时出现。仔细阅读源码就知道,使用迭代器遍历元素时由一个计数器,这个计数器就是为“快速失败原创 2016-04-08 14:48:35 · 6713 阅读 · 0 评论 -
Java并发编程系列之二十七:ThreadLocal
ThreadLocal简介ThreadLocal翻译过来就是线程本地变量,初学者可能以为ThreadLocal是指一个Thread,其实说白了,ThreadLocal就是一个成员变量,只不过这是一个特殊的变量——变量值总是与当前线程(调用Thread.currentThread()得到)相关联。既然ThreadLocal是一个变量,那么其作用是是什么呢?说得抽象点就是提供了线程封闭性,说得具体点就是原创 2016-04-08 22:19:25 · 3288 阅读 · 0 评论 -
Java并发编程系列之三十:多线程的代价
线程的简单回顾在操作系统中引入多线程的原因是进程切换的开销太大,进程在进行上下文切换时由于要切换页表,往往伴随者页调度,因此开销比较大,而线程在进行上下文切换时,由于仅涉及与自身相关的寄存器状态和栈的信息(线程的上下文环境主要包含寄存器的值、程序计数器、栈指针),因此开销比较小。所以,充分利用多线程可以提供系统的执行效率以及充分利用资源。在多个线程进行协作完成任务时,由于涉及到资源的共享与数据的同步原创 2016-04-12 18:37:22 · 4165 阅读 · 0 评论 -
Java并发编程系列之二:线程状态
线程的状态一共有6种,在任意时刻线程的状态只能是其中的一种。正确理解线程的状态有助于我们更容易理解线程。具体的线程状态如下: 状态名称 说明 NEW 初始状态,线程被构建,但是还没有调用start方法 RUNNING 运行状态 BLOCKED 阻塞状态,表示线程阻塞于锁 WAITING 等待状态,表示线程线程进入等待状态,进入该状态后需要其他线程做出通知动作原创 2015-12-04 22:01:21 · 1264 阅读 · 2 评论 -
Java并发编程系列之三:重排序与顺序一致性
前言在我们编写程序并运行的时候,编译器给我们一个错觉:程序编译的顺序与编写的顺序是一致的。但是实际上,为了提高性能,编译器和处理器常常会对指令进行重排序。重排序主要分为两类:编译器优化的重排序、指令级别并行的重排序和内存系统的重排序。所以我们编写好Java源代码之后,会经过以上三个重排序,到最终的指令序列。我们这里提到的Java内存模型又是什么呢?Java内存模型(后面简称JMM)是语言级别的内存模原创 2015-12-01 15:41:10 · 6341 阅读 · 0 评论 -
Java并发编程系列之五:happens-before原则
前言happens-before是JMM的核心,之所以设计happens-before,主要出于以下两个方面的因素考虑的:1)程序员的角度,JMM内存模型需要易于理解、易于编程;2)编译器和处理器的角度,编译器和处理器希望内存模型对其束缚越少越好,这样就可以根据自己的处理规则进行优化。但是这两个方面其实是相互矛盾的,因为JMM易于编程和理解就意味着对编译器和处理器的束缚就越多。happens-bef原创 2015-12-02 14:34:46 · 6703 阅读 · 0 评论 -
Java并发编程系列之二十八:CompletionService
CompletionService简介CompletionService与ExecutorService类似都可以用来执行线程池的任务,ExecutorService继承了Executor接口,而CompletionService则是一个接口,那么为什么CompletionService不直接继承Executor接口呢?主要是Executor的特性决定的,Executor框架不能完全保证任务执行的异原创 2016-04-10 17:01:15 · 15802 阅读 · 6 评论 -
Java并发编程系列之三十一:过早的通知
等待通知机制在前面介绍了等待通知机制,并使用该机制实现了消费者-生产者模式。我们知道,一个因为调用wait的线程会进入等待队列,当有其他的线程通知的时候才会从等待队列中返回,线程状态会变为RUNNABLE。但是,反过来说,如果一个线程从wait方法中返回,是不是就一定意味着线程等待的条件满足了呢?答案是否定的。考虑这样的场景:比如两个人的手机铃声是一样的(音量和类型),那么当两个手机同时响的时候,就原创 2016-04-14 15:38:39 · 4052 阅读 · 0 评论 -
Java并发编程系列之三十二:丢失的信号
这里的丢失的信号是指线程必须等待一个已经为真的条件,在开始等待之前没有检查等待条件。这种场景其实挺好理解,如果一边烧水,一边看电视,那么在水烧开的时候,由于太投入而没有注意到水被烧开。丢失的信号指的就是这种情况。创建两个线程分别执行通知和等待方法,并且将执行通知的线程先与执行等待的线程,下面的代码演示了这点:package com.rhwayfun.patchwork.concurrency.r04原创 2016-04-14 16:35:29 · 4238 阅读 · 0 评论 -
Java并发编程系列文章目录帖及源码
为了方便对并发系列的文章进行更好的阅读,楼主在这里根据学习的顺序对文章了进行了整理,主要有两个目的:日后需要回顾这部分的知识也更方便阅读;增加新的文章的时候对文章的深度和难度也有一个了解。所以本着从入门到初步掌握(还没到精通的程度)并发编程的系列知识原则,汇总的文章目录如下:1、并发机制的底层原理2、重排序与顺序一致性3、锁与volatile的内存语义 4、happens-before原则5、队列原创 2016-04-18 15:48:02 · 3543 阅读 · 4 评论 -
Java并发编程系列之二十一:CountdownLatch
CountDownLatch是JDK提供的并发工具包,理解并掌握这些工具包的使用有助于简化特定场景下的编程。就CountDownLatch而言,允许一个或者多个线程等待其他线程完成操作。等待其他线程完成不是与Thread.join()方法类似吗,因为Thread.join()就是让当前的线程等待join的线程执行完毕再继续执行。这里基于一个简单的需求实现CountDownLatch的功能:读取某目录原创 2016-04-06 13:57:58 · 4163 阅读 · 0 评论 -
Java并发编程系列之二十:Fork/Join框架
Fork/Join框架简介 Fork/Join框架是Java 7提供的用于并行执行任务的框架。具体是把大任务切分为小任务,再把小任务的结果汇总为大任务的结果。从这两个单词的角度分析,Fork是分叉的意思,可以引申为切分,Join是加入的意思,可以引申为合并。Fork的作用是把大任务切分为小任务,Join则是把这些小任务的执行结果进行合并的过程。以计算1+2+3+4为例,假设阈值是2,那么Fork会原创 2016-04-06 12:11:24 · 7173 阅读 · 0 评论 -
Java并发编程系列之十九:原子操作类
原子操作类简介当更新一个变量的时候,多出现数据争用的时候可能出现所意想不到的情况。这时的一般策略是使用synchronized解决,因为synchronized能够保证多个线程不会同时更新该变量。然而,从jdk 5之后,提供了粒度更细、量级更轻,并且在多核处理器具有高性能的原子操作类。因为原子操作类把竞争的范围缩小到单个变量上,这可以算是粒度最细的情况了。原子操作类相当于泛化的volatile变量,原创 2016-04-05 22:22:34 · 14457 阅读 · 0 评论 -
Java并发编程系列之一:并发机制的底层原理
前言并发编程的目的是让程序运行更快,但是使用并发并不定会使得程序运行更快,只有当程序的并发数量达到一定的量级的时候才能体现并发编程的优势。所以谈并发编程在高并发量的时候才有意义。虽然目前还没有开发过高并发量的程序,但是学习并发是为了更好理解一些分布式架构。那么当程序的并发量不高,比如是单线程的程序,单线程的执行效率反而比多线程更高。这又是为什么呢?熟悉操作系统的应该知道,CPU是通过给每个线程分配时原创 2015-11-29 19:58:56 · 4397 阅读 · 1 评论 -
Java并发编程系列之九:AQS
AQS是队列同步器的简称,简单来说这个东西是JUC框架工具包和构建锁的基础,它使用一个int成员变量表示同步状态,通过内置的FIFO队列完成资源获取线程的排队工作。深刻理解AQS对后面常用的并发工具也掌握得更深刻。首先说说AQS和锁的区别吧:锁的底层是使用AQS实现的。锁是面向使用者的,锁定义了使用者与锁交互的接口,隐藏了具体的实现细节;AQS是面向锁的实现者的,它屏蔽了一些复杂的同步状态的管理,简原创 2016-04-03 15:54:23 · 1541 阅读 · 0 评论 -
Java并发编程系列之八:wait()、notify()和notifyAll()
代码一个线程修改一个对象的值,而另一个线程则感知到了变化,然后进行相应的操作,这就是wait()、notify()和notifyAll()方法的本质。具体体现到方法上则是这样的:一个线程A调用了对象obj的wait方法进入到等待状态,而另一个线程调用了对象obj的notify()或者notifyAll()方法,线程A收到通知后从对象obj的wait方法返回,继续执行后面的操作。可以看到以上两个线程通原创 2016-04-02 18:47:20 · 9792 阅读 · 1 评论 -
Java并发编程系列之七:正确终止与恢复线程
前面提到了stop()、suspend()等方法在终止与恢复线程的弊端,那么问题来了,应该如何正确终止与恢复线程呢?这里可以使用两种方法:interrupt()方法和使用boolean变量进行控制。在使用interrupt方法之前,有必要介绍一下中断以及与interrupt相关的方法。中断可以理解为线程的一个标志位属性,表示一个运行中的线程是否被其他线程进行了中断操作。这里提到了其他线程,所以可以认原创 2016-04-02 17:27:09 · 9182 阅读 · 1 评论 -
Java并发编程系列之六:stop()、resume()和suspend()
这三个方法已经是jdk是过期的方法,为什么仍然要单独拿出来说呢?主要目的是理解jdk多线程API设计的初衷,理解并且更好使用线程API。那么就来说说这三个方法吧:stop方法用于终止一个线程的执行,resume方法用于恢复线程的执行,suspend方法用于暂停线程的执行。要注意的resume方法需要和suspend方法配对使用,因为被暂停的线程需要执行恢复方法才能继续执行。虽然这三个方法不在推荐使用原创 2016-04-02 16:40:18 · 6446 阅读 · 1 评论 -
Java并发编程系列之十:synchronized(1)
在多线程并发访问资源(这类资源称为临街资源)的时候,由于割裂来了原子操作,所以会导致数据不一致的情况。为了避免这种情况,需要使用同步机制,同步机制能够保证多线程并发访问数据的时候不会出现数据不一致的情况。一种同步机制是使用synchronized关键字,这种机制也称为互斥锁机制,这就意味着同一时刻只能有一个线程能够获取到锁,获得的锁也被称为互斥锁。其他需要获取该互斥锁的线程只能被阻塞,直到获取到该锁原创 2016-04-03 16:54:26 · 4063 阅读 · 0 评论 -
Java并发编程系列之十一:synchronized(2)
上一篇文章中提到了synchronized关键字在实现同步方面的作用与原理,其实,除了互斥机制,synchronized还能实现内存可见性——当一个线程修改了对象的状态,其他线程可以看到状态的变化。关于synchronized的内存可见性做如下说明:线程B在执行由锁保护的同步代码块时,可以看到线程A对同一个锁保护的同步代码块的操作结果。举个例子,A获取对象M的对象级别锁,并进入代码块,随后释放对象原创 2016-04-03 17:12:58 · 1100 阅读 · 0 评论 -
Java并发编程系列之十二:死锁、饥饿和活锁
死锁发生在一个线程需要获取多个资源的时候,这时由于两个线程互相等待对方的资源而被阻塞,死锁是最常见的活跃性问题。这里先分析死锁的情形:假设当前情况是线程A已经获取资源R1,线程B已经获取资源R2,之后线程A尝试获取资源R2,这个时候因为资源R2已经被线程B获得了,所以线程A只能阻塞直到线程B释放资源R2。另一方面,线程B在已经获得资源R2的前提下尝试获取由线程A持有的资源R1,那么由于资源R1已经被原创 2016-04-03 18:28:02 · 9309 阅读 · 3 评论 -
Java并发编程系列之十三:生产者-消费者模式
生产者-消费者模式指的是:生产者和消费者在同一个时间段共用同一段空间,在这段时间内,生产者负责往存储空间生产数据,而消费者则负责消费数据。实际上存在很多类似的场景:消息中间件就可以看做这种模式的应用,客户端请求负责把请求消息发送给消息中间件,然后由服务器负责从消息中间件获取请求并进行响应;还有网络编程中Socket也可以看做是生产者消费者模式的应用,etc。为了简化问题的研究,现在仅仅考虑一种简单的原创 2016-04-04 13:46:14 · 5135 阅读 · 0 评论 -
Java并发编程系列之十四:阻塞队列
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加操作支持阻塞地插入和移除方法。支持阻塞插入的方法是指当队列满时会阻塞插入元素的线程,直到队列不满;支持阻塞移除的方法是指当队列为空时获取元素的线程无法继续获取元素直到队列不空。可以发现阻塞队列非常适合消费者和生产者场景下进行使用,生产者生产数据就是向阻塞队列中插入元素,消费者消费数据就是从阻塞队列中移除元素。Java提供了原创 2016-04-04 15:48:08 · 1653 阅读 · 0 评论 -
Java并发编程系列之十五:Executor框架
Java使用线程完成异步任务是很普遍的事,而线程的创建与销毁需要一定的开销,如果每个任务都需要创建一个线程将会消耗大量的计算资源,JDK 5之后把工作单元和执行机制区分开了,工作单元包括Runnable和Callable,而执行机制则由Executor框架提供。Executor框架为线程的启动、执行和关闭提供了便利,底层使用线程池实现。使用Executor框架管理线程的好处在于简化管理、提高效率,还原创 2016-04-04 17:55:04 · 9223 阅读 · 3 评论 -
Java并发编程系列之十六:Lock锁
Lock锁简介Lock锁机制是JDK 5之后新增的锁机制,不同于内置锁,Lock锁必须显式声明,并在合适的位置释放锁。Lock是一个接口,其由三个具体的实现:ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,即重入锁、读锁和写锁。增加Lock机制主要是因为内置锁存在一些功能上局限性。比如无法中原创 2016-04-05 13:50:42 · 4804 阅读 · 0 评论 -
Java并发编程系列之十七:Condition接口
通过前面的文章,我们知道任何一个Java对象,都拥有一组监视器方法,主要包括wait()、notify()、notifyAll()方法,这些方法与synchronized关键字配合使用可以实现等待/通知机制。而且前面我们已经使用这种方式实现了生产者-消费者模式。类似地,Condition接口也提供类似的Object的监视器的方法,主要包括await()、signal()、signalAll()方法,原创 2016-04-05 14:50:55 · 7276 阅读 · 0 评论 -
Java并发编程系列之十八:读写锁
在之前提到的synchronized的互斥锁和ReentrantLock都属于排他锁,这些锁在同一时刻只能允许一个线程进行访问。而读写锁允许同一时刻有多个读线程进行访问,但是在有写线程的时候,所有的读线程和其他所有的写线程都将阻塞。读写锁维护了一对锁,一个读锁和一个写锁,这种分离提高了并发性,因为在使用排他锁的时候,读读线程也是被阻塞的,相比之下确实提高了并行度。读写锁除了保证写操作对读操作的可见性原创 2016-04-05 16:20:53 · 5053 阅读 · 0 评论 -
Java并发编程系列之二十九:正确终止与恢复线程(续)
重新认识中断之前在正确终止与恢复线程一文中介绍了使用Thread类的interrupt方法和使用标志位实现线程的终止。由于之前只是简单介绍了jdk默认中断方法的问题,对线程的中断机制没有深入介绍。为了正确终止线程,深刻理解线程中断的本质是很有必要的。Java没有提供可抢占的安全的中断机制,但是Java提供了线程协作机制(之前说的interrupt方法和标志位本质上都属于线程之间协作的手段),但是提供原创 2016-04-11 19:27:56 · 3745 阅读 · 4 评论