Java并发编程艺术读书笔记
1、多线程在CPU切换过程中,由于需要保存线程之前状态和加载新线程状态,成为上下文切换,上下文切换会造成消耗系统内存。所以,可合理控制线程数量。 如何控制:
(1)使用ps -ef|grep appname,查找appname的pid;如1111
(2)使用jstack 1111 > /home/ibethfy/dump1,将dump信息追加到dump1
(3)使用grep java.lang.Thread.State /home/ibethfy/dump1 | awk '{print $2$3$4$5}' | sort | uniq -c,统计查找线程状态个数。分析是否WAITING的线程过多。
(4)查看这些WAITING的线程时怎么产生的,如某个服务创建线程池中线程过多,分析后对应修改。
(5)重启后重新统计。
2、避免死锁几种常用方式
(1)避免一个线程同时获取多个锁。
(2)避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
(3)尝试使用定时锁,lock.tryLock(timeout)来替代内部锁机制。
(4)对于数据库锁,加锁和解锁必须在同一个连接内,不然会造成解锁失败的情况。
3、volatile保证共享资源对其他线程的可见性。volatile修饰共享变量时,共享变量进行写操作时,会在汇编中多一个lock前缀的指令,该指定表示要将该变量从当前处理器的缓存行数据写到系统内存,写会内存操作会使其他CPU缓存了该内存地址的数据失效。其他CPU去获取该数据时,会从系统内存重新获取。
4、synchronized,JVM通过进入和退出Monitor对象来实现代码块和方法同步。代码块同步使用的是monitorenter和monitorexit来实现。在编译时,会将这两个指令分别插入到代码块开始和结束(包括正常和异常结束),在执行这两句指令时,会去进去和退出monitor对象。
5、锁在哪?Java的锁,是存在对象头的Mark Word中。存储一个锁地址,指向C++里面的一个ObjectMonitor对象,该对象存储了当前占用锁的线程,等待的线程,该线程重入的次数,锁状态等字段。
6、JMM,Java内存模型,用于控制Java线程之间的通信,和计算机多线程模型类似,也是为每个线程分配了类似的内存缓存,变量存在主内存中,要保证线程之间的通信,必须要保证线程A将数据从本地缓存刷新到主内存,线程B识别到数据更新,从主内存中取得最新数据。JMM主要就是控制主内存和线程本地内存之间的交互等。
7、JMM模型中,主要是解决有序性(顺序一致性),可见性,原子性。
(1)由于多线程中,每个线程有自己的本地缓存,且在执行代码后,不知道何时回把缓存刷新到主内存,所以,线程的操作对其他线程是不可见的。这时不同线程获取共享变量就会出现不一致的问题。
(2)指令的重排序:重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。数据依赖性:某一行代码依赖上一行代码,这样不会进行这两条代码的重排序,多线程系统不会考虑该问题。顺序一致性:指令重排序后,最终执行结果要和顺序执行结果一致。
(3)多线程之间进行了正确的数据同步,JMM保证多个线程的顺序一致性。
(4)顺序一致性模型:一个程序的操作必须按照程序顺序执行。不管程序是否同步,所有线程都能按照单一的操作执行顺序。即多个线程的操作可以并行执行,但对于单个线程来看,他的指令时顺序执行的,且每条指令都对其他线程可见。
(5)JMM不保证顺序一致性,即未同步的线程,执行顺序是无序的,且可能对其他内存不可见。这个时候,就需要同步来实现顺序一致性模型。synchronized修饰的代码块,在获得锁后才能执行,从而保证了多个线程的整体顺序执行,但同步块局部不依赖的指令,也可进行重排序(尽量给编译器或者优化器优化提供入口)。为什么JMM不保证未同步的线程的顺序一致性呢?主要是要保证会禁止大量的编译器等优化,严重影响性能,所以就提供同步方法来实现。
(6)JMM内存可见性的保证:单线程不会出现内存可见性问题,编译器,runtime,处理器会共同保证期执行结果和顺序一致性模型结果一致;多线程,正确同步的多线程将具有顺序一致性,JMM通过限制编译器和处理器的重排序来提供内存可见性的保证。未同步或未正确同步的多线程,JMM为他们提供了最小安全性的保证,线程读取到的值,要么是之前线程写入的值,要么是初始值。
(7)volatile,当写volatile时,其他读写指令在其之后执行,保证了有序性;当写时,会把新的缓存中的值写入到主内存;另一线程读时,会将本地缓存地址置为无效,从主内存中获取该值。
(8)synchronized,当某线程获得锁时,其余线程等待,保证了有序性。当某线程释放锁时,会将本地缓存写入主内存,当另一线程获取锁时,会将本地缓存置为无效,从主内存获取数据。所以保证了多线程的顺序一致性和可见性。
(9)happens-before规则。
8、wait和notify:WaitThread首先获取了对象的锁,然后调用锁对象的wait()方法,从而放弃了锁并进入了对象的等待队列WaitQueue中,进入等待状态。由于WaitThread释放了对象的锁,NotifyThread随后获取了对象的锁,并调用锁对象的notify()方法,将WaitThread从WaitQueue移到SynchronizedQueue中,此时WaitThread的状态变为阻塞状态。NotifyThread释放了锁之后,WaitThread再次获取到锁并从wait()方法返回继续执行。金典案例如下: synchronized(对象) {
while(条件不满足) {
对象.wait();
}
对应的处理逻辑
}
synchronized(对象) {
改变条件
对象.notifyAll();
}
9、ThreadLocal:ThreadLocal,即线程变量,是一个以ThreadLocal线程对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
10、CountDownLatch:
(1)、初始化CountDownLatch cdl = new CountDownLatch(5);
(2)、同一线程或多个线程调用cdl.countDown(),使计数器减一,通过入参传入cdl;等待线程调用cdl.await(),阻塞等待cdl计数器等于0后,执行。
11、CyclicBarrier:
(1)、初始化CyclicBarrier cb = new CyclicBarrier();
(2)、其余线程调用cb.await(),告诉屏障我已到达,然后等待指定数量的线程都已到达后,然后统一往后执行。
(3)、构造函数可传入另一线程,表示到达屏障的数量满足后,优先执行该方法。
12、Semaphore:Semaphore(10)表示允许10个线程获取许可证,也就是最大并发数是10。Semaphore的用法也很简单,首先线程使用Semaphore的acquire()方法获取一个许可证,使用完之后调用release()方法归还许可证。还可以用tryAcquire()方法尝试获取许可证。
13、Exchanger:用于线程间数据交换。
14、线程池技术:好处
(1)降低系统消耗,重复利用线程
(2)提高响应速度,不用重新创建线程
(3)提高可管理性,可以实现对线程的管理。
线程池流程:
(1)新来一个任务,如果没有超过核心线程数,则创建一个核心线程,执行任务,不管是否有闲置的核心线程,也会创建新的核心线程执行任务。
(2)如果核心线程都处于运行状态,且数量已达上限,则判断工作队列是否已满,如果未满,则将任务加入工作队列,排队等待核心线程空闲后执行。
(3)如果核心线程都在运行任务和工作队列都满了,则创建新的额外线程来执行任务。(额外线程由最大线程决定,且无用后会被回收)
(4)额外线程也满了且都在执行的话,就采用拒绝策略拒绝请求。
线程池创建参数:
(1)corePoolSize,核心线程数量。
(2)runnableTaskQueue:任务队列,有ArrayBlockingQueue,基于数组的有界阻塞队列,FIFO;LinkedBlockingQueue,基于链表实现的阻塞队列,FIFO,吞吐量通常高于ArrayBlockingQueue,静态工厂方法Executors.newFixedThreadPool()使用了这个队列;SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列;
(3)maximumPoolSize:线程池最大线程数,线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界的任务队列这个参数就没什么效果。
(4)ThreadFactory:用于设置创建线程的工厂;
(5)RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下四种策略: ·AbortPolicy:直接抛出异常。 ·CallerRunsPolicy:只用调用者所在线程来运行任务。 ·DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。 ·DiscardPolicy:不处理,丢弃掉
(6)keepAliveTime(线程活动保持时间):线程池的工作线程空闲后(非核心线程会回收,allowCoreThreadTimeOut(true),设置这个属性,核心线程在超时未用后,也会被回收),保持存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
(7)TimeUnit(线程活动保持时间的单位);
Java并发编程艺术读书笔记的更多相关文章
- Java并发编程实战 读书笔记(一)
最近在看多线程经典书籍Java并发变成实战,很多概念有疑惑,虽然工作中很少用到多线程,但觉得还是自己太弱了.加油.记一些随笔.下面简单介绍一下线程. 一 线程与进程 进程与线程的解释 个人觉 ...
- Java并发编程实战 读书笔记(二)
关于发布和逸出 并发编程实践中,this引用逃逸("this"escape)是指对象还没有构造完成,它的this引用就被发布出去了.这是危及到线程安全的,因为其他线程有可能通过这个 ...
- Java并发编程实践读书笔记(5) 线程池的使用
Executor与Task的耦合性 1,除非线程池很非常大,否则一个Task不要依赖同一个线程服务中的另外一个Task,因为这样容易造成死锁: 2,线程的执行是并行的,所以在设计Task的时候要考虑到 ...
- Java并发编程实践(读书笔记) 任务执行(未完)
任务的定义 大多数并发程序都是围绕任务进行管理的.任务就是抽象和离散的工作单元. 任务的执行策略 1.顺序的执行任务 这种策略的特点是一般只有按顺序处理到来的任务.一次只能处理一个任务,后来其它任 ...
- Java并发编程实践读书笔记(2)多线程基础组件
同步容器 同步容器是指那些对所有的操作都进行加锁(synchronize)的容器.比如Vector.HashTable和Collections.synchronizedXXX返回系列对象: 可以看到, ...
- Java并发编程实践读书笔记(1)线程安全性和对象的共享
2.线程的安全性 2.1什么是线程安全 在多个线程访问的时候,程序还能"正确",那就是线程安全的. 无状态(可以理解为没有字段的类)的对象一定是线程安全的. 2.2 原子性 典型的 ...
- Java并发编程实践读书笔记(4)任务取消和关闭
任务的取消 中断传递原理 Java中没有抢占式中断,就是武力让线程直接中断. Java中的中断可以理解为就是一种简单的消息机制.某个线程可以向其他线程发送消息,告诉你“你应该中断了”.收到这条消息的线 ...
- Java并发编程实践读书笔记(3)任务执行
类似于Web服务器这种多任务情况时,不可能只用一个线程来对外提供服务.这样效率和吞吐量都太低. 但是也不能来一个请求就创建一个线程,因为创建线程的成本很高,系统能创建的线程数量是有限的. 于是Exec ...
- 如何评价《Java 并发编程艺术》这本书?
对于书评这件事情,我其实是不想写的,因为每个人都有自己的一个衡量标准,每个人眼中都有自己的哈姆雷特,是好是坏每个人都褒贬不一.如果对于书中的知识你都掌握了,你只是想把它作为一种知识串联的记忆体的话,那 ...
随机推荐
- C#中实现文件重命名的方式
场景 在C#中如果是删除文件的话可以直接使用 if (System.IO.File.Exists(fileName)) { System.IO.File.Delete(fileName); } 但是如 ...
- HTML5☞canvas
<canvas>便签用于绘制图像,图表.不过,<canvas> 元素本身并没有绘制能力(它仅仅是图形的容器) - 您必须使用脚本JavaScript来完成实际的绘图任务.既然你 ...
- Flutter学习笔记(6)--Dart流程控制语句
如需转载,请注明出处:Flutter学习笔记(5)--Dart流程控制语句 条件语句:if.if...elseif.if...elseif...else ; ) { print('优秀'); } &g ...
- 微信小程序根据生日获取年龄
// 根据出生日期计算年龄周岁 传参格式为1996-06-08 // 根据出生日期计算年龄周岁 传参格式为1996-06-08 function getAge(strBirthday) { var r ...
- Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises)
传送门 A. Optimal Currency Exchange 枚举一下就行了. Code #include <bits/stdc++.h> using namespace std; t ...
- MongoDB学习笔记(一、MongoDB入门)
目录: 为什么要使用nosql mongo的简介 应用场景 入门demo 为什么要使用nosql: 随着互联网的发展,用户数量激增,访问量的上涨,传统的关系型数据库的性能也趋于瓶颈. 关系型数据库难以 ...
- WPF 精修篇 拖拽 DragDrop
原文:WPF 精修篇 拖拽 DragDrop WPF 实现拖拽 效果 <Grid> <Grid.ColumnDefinitions> <ColumnDefinition ...
- CF732D Exams
这题可以用二分答案来做 那么为什么可以用二分答案呢? 答案当然是满足了单调性. 假设用\(x\)天能够考完所有试,那么用大于$x $天必定也能够考完所有试,所以满足了单调性,我们就可以二分答案 那么如 ...
- __module__和__class__
目录 一.__module__ 二.__class__ # lib/aa.py class C: def __init__(self): self.name = 'SB' # index.py fro ...
- Ubuntu 修改默认编辑器
sudo update-alternatives --config editor