本文主要是在拜读了《java并发编程的艺术》之后的一个总结,对相关重点进行结构性的梳理。这本书写的还是相当赞的,还是比较符合个人的思维方式。《java并发编程实战》阅读起来还是相对晦涩些,建议读者先看《java并发编程的艺术》,再啃《java并发编程实战》这本书,并没有变低或者抬高谁的意思。
some words
juc包是jdk1.5之后引入的,并且是以api的方式,是一个叫Doug Le的大神写的。那问题来了,既然是一个人写的,那为什么不是我写的,既然是一个人写的 ,为什么早不写晚不写恰恰在jdk1.5时引入。这就涉及到一个背景,从jdk1.5开始,java使用新的JSR-133内存模型,而JSR-133增强了volatile关键字的内存语义,这只是一个开始。由下至上的总结下:
volatile、CAS、final
像指令重排序、内存屏障这些词汇可以查找相关文献 理解下到底是什么含义。volatile它的作用就是保证数据的可见性,volatile变量写的内存语义:当写一个volatile变量时,jmm会把该线程对应的本地内存中的共享变量值刷新到主内存中;volatile变量读的内存语义:当读一个volatile变量时,jmm会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量(普通变量 线程都会在自己本地内存拷贝一份,写的时候并不会刷主存,读的时候直接拿来用,并发时作为共享变量当然会有问题了)。那volatile怎么做到的呢?其实在volatile写操作之前会插入一个SS屏障,写操作之后插入一个SL屏障。volatile读操作之后插入一个LL屏障和一个LS屏障。屏障对应到底层就是一个特殊的指令了,反正目的就是避免volatile变量和其他变量的重排序,并保证可见性。像java语言内置的Synchronized关键字编译之后底层也是插入了相关的特殊指令。
CAS(compare and swap)是一个组合操作(读-改-写),但是它会保证操作的原子性,它基于处理器提供的高效机器级别的原子指令,依赖底层硬件支持。更深入的介绍可以查看相关资料。
volatile和CAS是juc包的基石。顺带介绍下final关键字,它的作用就是final的引用不能从构造函数中“溢出”,什么意思?就是在构造一个对象时,它里面的final域会先初始化。这是在final关键字修辞成员变量时的作用,在修辞局部变量或者参数时,仅仅是保证final引用不可变而已,编译之后相关信息会被擦除。
AQS
AbstractQueuedSynchronizer(同步器)是用来构建锁或者其他同步组件的基础框架。它是一个模板类,子类可重写的五个方法有:tryAcquire(),tryRelease(),tryAcquireShared(),tryReleaseShare()及isHeldExclusively()。第一二个方法独占锁使用,三四个方法共享锁使用。第五个方法表示是否被当前线程所独占。它里面有一个volatile int state 变量,用于记录状态,还有一个FIFO的CLH队列,这个队列持有被阻塞线程的引用。具体细节可以查看相关资料
锁及同步组件
这里介绍几个比较典型的类。其实他们大多数都是实现了Lock接口然后定义一个内部类继承于AQS(为啥用内部类,可自行google),或者组合相关类。
ReentrantLock 它是一个可重入的排他锁,这点和Synchronized的语义类似。
ReadWriteLock 读写锁 。好奇的是AQS只有一个state变量,它是怎么做到同时记录读、写两个状态的。其实 它把state变量分为了两部分,高16位用于记录读状态,低16位用于记录写状态。使用场景当然是用于读多写少的 场景了。
CountDownLatch 底层实现很简单,内部类直接实现了AQS。
ConcurrentHashMap 里面segment继承了ReentrantLock 。分segment锁,锁的粒度小,就可以并发访问了。写操作是加锁的,读不加锁。解决hash冲突的方式和hashmap一样,放在链表头,为什么要放在链表头,因为next属性是final的。
这里说到了锁Lock,锁当然会阻塞线程了。它是怎么阻塞线程的呢?调用LockSupport类的park()方法,唤醒线程用unpark()方法。park()和unpark()方法是如何阻塞和唤醒线程的呢?它其实是调用了Unsafe类提供的方法,Unsafe是如何阻塞和唤醒线程呢?其实就是一个本地系统调用了,声明为native的方法。unsafe是个后门类,有很多超级方法。我们不能直接使用Unsafe类,它进行了安全认证,但我们可以通过反射来使用。具体细节可以google
Executor框架
我们知道线程池处理任务的时候。处理流程是 :核心线程(CorePoreSize)池->queue->最大线程(maximumPoolSize),其中核心线程数和最大线程数这两步会获取全局锁,这会是一个严重的伸缩瓶颈(通常都会实现预热Executor使线程数大于或等于CorePoreSize)。线程池里的线程之所以不停的执行任务,是因为线程的run方法是一个for()无限循环,在里面不停的执行我们提交的Runnable任务。
我们知道了增长的顺序,那如何线程怎么回收呢。默认情况下如果普通线程获取不到任务的这个线程将会被回收,获取任务的方法就是poll(),获取不到任务就会退出上面说的for循环。它有个时间参数,这个时间就是我们初始化线程池时设置的timeOut参数。逻辑就是如果线程空闲时间到了timeOut这个线程会被回收。这个是最大线程减少逻辑。那核心线程什么时候被回收呢?正常情况下核心线程是不会被回收的,因为核心线程获取任务的方法是take(),如果获取不到任务它将被一直阻塞,所以 如果我们初始化了一个线程池但是没有shutDown(),将会导致整个jvm进程不会退出,因为核心线程一直没有被回收呢。那不正常情况是啥,我们设置allowCoreThreadTimeOut为true就行了(默认为false),这样核心线程获取任务的方法变为poll(),超时获取不到任务就会被回收了。
本文只是简单的介绍了下juc包,具体细节还请查阅相关书籍。系统的组织看到的东西便于记忆,不然时间久来容易忘记。纯文字记录阅读起来相对枯燥。
参考资料:
Java并发编程的艺术(方腾飞、魏鹏、程晓明)
http://www.cnblogs.com/nullzx/p/5175574.html
http://extremej.itscoder.com/threadpoolexecutor_source/
<script type="text/javascript">
$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('\n').length;
var $numbering = $('<ul/>').addClass('pre-numbering').hide();
$(this).addClass('has-numbering').parent().append($numbering);
for (i = 1; i <= lines; i++) {
$numbering.append($('<li/>').text(i));
};
$numbering.fadeIn(1700);
});
});
</script>
分享到:
相关推荐
在Java领域内,`java.util.concurrent`(简称J.U.C)包提供了强大的并发工具,极大地简化了多线程编程的复杂度。本文将基于给定文件提供的信息,深入探讨J.U.C框架的核心概念之一——`AbstractQueuedSynchronizer`...
Java并发编程是Java程序员需要掌握的关键技能之一,而J.U.C(Java Concurrency Utilities)是Java平台中的核心组件,提供了丰富的工具和接口来处理多线程环境中的并发问题。本资料"J.U.C系列线程安全的理论讲解编程...
《JAVA并发编程与高并发解决方案-并发编程四之J.U.C之AQS》是一篇详细介绍Java实用并发工具包(Java Util Concurrency,简称J.U.C.)中重要组成部分——AbstractQueuedSynchronizer(简称AQS)的文章。AQS是Java并发...
在Java中,为了提供高性能、低延迟的并发数据结构,Java提供了多种并发容器类,这些类主要位于`java.util.concurrent`包内,通常被称为J.U.C(Java Util Concurrency)容器。 ##### 1. CopyOnWriteArrayList `...
CountDownLatch是Java并发编程中一个重要的同步工具类,它由Java 1.5引入,位于`java.util.concurrent`包下。这个工具类主要用于协调多个线程间的协作,使得某个线程(或一组线程)必须等待其他线程完成指定的任务后...
Java并发工具包(Java Concurrency Utilities,简称J.U.C)是Java编程语言中的一个核心组件,它提供了丰富的类和接口,用于高效地处理多线程环境中的并发问题。这个工具包在Java 5.0版本中引入,极大地提升了开发者...
Java并发编程库,特别是java.util.concurrent(简称J.U.C),是Java语言在多线程处理上的一大亮点。并发编程是一个复杂的话题,因为它涉及到许多高级概念,包括线程安全、死锁、性能优化和原子操作等。J.U.C正是为了...
Java并发工具包(J.U.C)是Java编程语言中用于并发编程的一系列工具包的统称,它包含了一系列方便实现多线程编程的类和接口,使得开发者可以更加方便地编写高效、线程安全的程序。本文将深入浅出地探讨J.U.C的原理和...
log4j.appender.CONSOLE.layout.ConversionPattern=[framework] %d- %c -%-4r [%t] %-5p %c %x - %m%n ``` 在 SecureCRT 环境中配置 JDK 需要满足一定的条件,并且需要注意一些细节问题。同时,写一个 Java 程序来...
2.2.1 G C C 简介......................................................19 2 . 2 . 2 编译程序的基本知识......................................................................................... 21 2.2.3 ...
#### J.U.C的整体认识 Java的并发编程模型在J.U.C(`java.util.concurrent`)包中得到了全面的展现,这不仅仅是Java语言本身的一大亮点,更是多线程编程领域的重要组成部分。本文旨在通过深入浅出的方式探讨J.U.C的...
Java是一种广泛使用的高级编程语言,由Sun Microsystems(后被Oracle公司收购)于1995年推出。它的设计目标是“简单、面向对象、健壮、安全、高性能、平台独立和可移植性”。这个“java最新脑图.zip”文件很可能包含...
标题中的“Jflash.rar”指的是一个RAR格式的压缩文件,通常用于存储多个相关文件或程序。在这个特定的情况下,它包含了与TQ2440开发板相关的JTAG烧写程序的源码。JTAG(Joint Test Action Group)是一种标准的硬件...
(b) 当\( x = u + v \)时,\( g(u+v) = (u+v)^2 - 4(u+v) + 4 \)。 (c) 当\( x = e^{jt} \)时,\( g(e^{jt}) = e^{2jt} - 4e^{jt} + 4 \)。 (d) 当\( x = t \)时,\( g(g(t)) = [t^2 - 4t + 4]^2 - 4[t^2 - ...
J.U.C并发包,即java.util.concurrent包,是JDK的核心工具包,是JDK1.5之后,由 Doug Lea实现并引入。
log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n ``` 这段配置将日志级别设置为DEBUG,并将日志输出到Tomcat的`logs`目录下的`myapp.log`文件,每天滚动一次。 3....
return e.m=t,e.c=r,e.d=function(t,r,i){e.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return ...
AQS是J.U.C包下AbstractQueuedSynchronizer抽象的队列式的同步器的简称,这是一个抽象类,它定义了一套多线程访问共享资源的同步器框架,J.U.C包下的许多同步类实现都依赖于它,比如ReentrantLock/Semaphore/...
-c是表示产生新的包,-f指定包的文件名。 # tar -rf all.tar *.gif 这条命令是将所有.gif的文件增加到all.tar的包里面去。-r是表示增加文件的意思。 # tar -uf all.tar logo.gif 这条命令是更新原来tar包all....