《Java并发编程实战》读书笔记
Subsections
线程安全(Thread safety)
锁(lock)
共享对象
对象组合
基础构建模块
任务执行
取消和关闭
线程池的使用
性能与可伸缩性
并发程序的测试
显示锁
原子变量和非阻塞同步机制
- public class Instance() {
- private Instance in = null;
- public Instance getInstance() {
- if(in == null) {
- in= new Instance();
- }
- return in;
- }
- }
原理:每个运行的线程都会有一个类型为ThreadLocal.ThreadLocalMap的map,这个map就是用来存储与这个线程绑定的变量,map的key就是ThreadLocal对象,value就是线程正在执行的任务中的某个变量的包装类Entry.
ThreadLocal有四个方法:
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null)
- return (T)e.value;
- }
- return setInitialValue();
- }
//如果是第一次调用,需要初始化。
- private T setInitialValue() {
- T value = initialValue();
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- return value;
- }
//将此线程局部变量的当前线程副本中的值设置为指定值
- public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
//移除此线程局部变量的值。
- public void remove() {
- ThreadLocalMap m = getMap(Thread.currentThread());
- if (m != null)
- m.remove(this);
- }
数据库连接管理类,转载:http://blog.csdn.net/ghsau/article/details/15732053
- public class ConnectionManager {
- /** 线程内共享Connection,ThreadLocal通常是全局的,支持泛型 */
- private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
- public static Connection getCurrConnection() {
- // 获取当前线程内共享的Connection
- Connection conn = threadLocal.get();
- try {
- // 判断连接是否可用
- if(conn == null || conn.isClosed()) {
- // 创建新的Connection赋值给conn(略)
- // 保存Connection
- threadLocal.set(conn);
- }
- } catch (SQLException e) {
- // 异常处理
- }
- return conn;
- }
- /**
- * 关闭当前数据库连接
- */
- public static void close() {
- // 获取当前线程内共享的Connection
- Connection conn = threadLocal.get();
- try {
- // 判断是否已经关闭
- if(conn != null && !conn.isClosed()) {
- // 关闭资源
- conn.close();
- // 移除Connection
- threadLocal.remove();
- conn = null;
- }
- } catch (SQLException e) {
- // 异常处理
- }
- }
- }
- public class PersonSet{
- private final Set<Person> mySet = new HashSet<Person>();
- public sychronized void addPersion(Person p) {
- mySet.add(p)
- }
- public sychronized boolean containsPerson(Person p) {
- return mySet.contains(p);
- }
- }
- public class privateLock {
- private final Object myLock = new Object();
- private int weight;
- void someMethod() {
- synchronized(myLock) {
- //访问weight
- }
- }
- }
使用私有锁对象比使用对象的内置锁有许多优点。私有锁可以将锁封装起来,客户代码无法得到锁。但客户可以通过公有方法来访问锁。以便参与到同步策略中去。
监视器好比一做建筑,它有一个很特别的房间,房间里有一些数据,而且在同一时间只能被一个线程占据,进入这个建筑叫做"进入监视器",进入建筑中的那个特别的房间叫做"获得监视器",占据房间叫做"持有监视器",离开房间叫做"释放监视器",离开建筑叫做"退出监视器".
如上图所示,一个线程通过1号门进入Entry Set(入口区),如果在入口区没有线程等待,那么这个线程就会获取监视器成为监视器的owner,然后执行监视区域的代码。如果在入口区中有其它线程在 等待,那么新来的线程也会和这些线程一起等待。线程在持有监视器的过程中,有两个选择,一个是正常执行监视器区域的代码,释放监视器,通过5号门退出监视 器;还有可能等待某个条件的出现,于是它会通过3号门到Wait Set(等待区)休息,直到相应的条件满足后再通过4号门进入重新获取监视器再执行。
注意:当一个线程释放监视器时,在入口区和等待区的等待线程都会去竞争监视器,如果入口区的线程赢了,会从2号门进入;如果等待区的线程赢了会从4 号门进入。只有通过3号门才能进入等待区,在等待区中的线程只有通过4号门才能退出等待区,也就是说一个线程只有在持有监视器时才能执行wait操作,处于等待的线程只有再次获得监视器才能退出等待状态。
信号量(Semaphore):用来控制同时访问某个特定资源的操作数量。通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。Semaphore允许线程获取许可, 未获得许可的线程需要等待.这样防止了在同一时间有太多的线程执行。Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
- package my.concurrent.semaphore;
- import java.util.concurrent.Semaphore;
- public class Car implements Runnable {
- private final Semaphore parkingSlot;
- private int carNo;
- /**
- * @param parkingSlot
- * @param carName
- */
- public Car(Semaphore parkingSlot, int carNo) {
- this.parkingSlot = parkingSlot;
- this.carNo = carNo;
- }
- public void run() {
- try {
- parkingSlot.acquire();
- parking();
- sleep(300);
- parkingSlot.release();
- leaving();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- private void parking() {
- System.out.println(String.format("%d号车泊车", carNo));
- }
- private void leaving() {
- System.out.println(String.format("%d号车离开车位", carNo));
- }
- private static void sleep(long millis) {
- try {
- Thread.sleep(millis);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- package my.concurrent.semaphore;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.Semaphore;
- public class ParkingCars {
- private static final int NUMBER_OF_CARS = 30;
- private static final int NUMBER_OF_PARKING_SLOT = 10;
- public static void main(String[] args) {
- /*
- * 采用FIFO, 设置true
- */
- Semaphore parkingSlot = new Semaphore(NUMBER_OF_PARKING_SLOT, true);
- ExecutorService service = Executors.newCachedThreadPool();
- for (int carNo = 1; carNo <= NUMBER_OF_CARS; carNo++) {
- service.execute(new Car(parkingSlot, carNo));
- }
- sleep(3000);
- service.shutdown();
- /*
- * 输出还有几个可以用的资源数
- */
- System.out.println(parkingSlot.availablePermits() + " 个停车位可以用!");
- }
- private static void sleep(long millis) {
- try {
- Thread.sleep(millis);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
4号车泊车
9号车泊车
2号车泊车
8号车泊车
10号车泊车
3号车泊车
12号车泊车
14号车泊车
6号车泊车
2号车离开车位
4号车离开车位
6号车离开车位
1号车离开车位
9号车离开车位
3号车离开车位
5号车泊车
8号车离开车位
10号车离开车位
11号车泊车
7号车泊车
12号车离开车位
13号车泊车
14号车离开车位
16号车泊车
17号车泊车
20号车泊车
19号车泊车
18号车泊车
15号车泊车
5号车离开车位
20号车离开车位
18号车离开车位
22号车泊车
11号车离开车位
7号车离开车位
13号车离开车位
15号车离开车位
21号车泊车
26号车泊车
23号车泊车
28号车泊车
25号车泊车
16号车离开车位
27号车泊车
17号车离开车位
30号车泊车
24号车泊车
29号车泊车
19号车离开车位
25号车离开车位
24号车离开车位
22号车离开车位
26号车离开车位
28号车离开车位
30号车离开车位
21号车离开车位
23号车离开车位
27号车离开车位
29号车离开车位
10 个停车位可以用!
- class singleThreadWebServer {
- ServerSocket socket = new ServerSocket(80) ;
- while (true) {
- Socket connection = socket.accept();
- handleRequest(connection);
- }
- }
- class MultiThreadWebServer {
- ServerSocket socket = new ServerSocket(80) ;
- while (true) {
- final Socket connection = socket.accept();
- Runnable task = new Runnable() {
- public void run() {
- handleRequest(connection);
- }
- }
- new Thread(task).start();
- }
- }
任务是一组逻辑工作单元,而线程则是使任务异步执行的逻辑单元。Executor为灵活且强大的异步任务执行框架提供了基础,还提供了对生命周期的支持,以及统计信息、应用管理机制和性能监视等机制。
Executor 基于生产者-消费者模式。提交任务相当是生产者,执行任务相当是消费者
a、执行策略:
任务在什么(What)线程中执行
任务以什么(What)顺序执行(FIFO/LIFO/优先级等)
同时有多少个(How Many)任务并发执行
允许有多少个(How Many)个任务进入执行队列
系统过载时选择放弃哪一个(Which)任务,如何(How)通知应用程序这个动作
任务执行的开始、结束应该做什么(What)处理
b、线程池:
线程池和工作者队列密切相关,工作者线程的任务:从工作队列中获取一个任务,执行任务,然后返回线程池并等待下一个任务。
Executors类里面提供了一些静态工厂,生成一些常用的线程池。
newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
c、线程池Executor任务拒绝策略
java.util.concurrent.RejectedExecutionHandler描述的任务操作。
第一种方式直接丢弃(DiscardPolicy)
第二种丢弃最旧任务(DiscardOldestPolicy)
第三种直接抛出异常(AbortPolicy)
第四种任务将有调用者线程去执行(CallerRunsPolicy)
d、生命周期
java.util.concurrent.ExecutorService 接口对象来执行任务,该接口对象通过工具类java.util.concurrent.Executors的静态方法来创建。 Executors此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
ExecutorService扩展了Executor并添加了一些生命周期管理的方法。一个Executor的生命周期有三种状态,运行 ,关闭 ,终止。Executor创建时处于运行状态。当调用ExecutorService.shutdown()后,处于关闭状态,isShutdown()方法返回true。这时,不应该再想Executor中添加任务,所有已添加的任务执行完毕后,Executor处于终止状态,isTerminated()返回true。
shutdown():执行平缓的关闭过程,不再接受新的任务,同时等待已经提交的任务执行完成。
shutdownNow();执行粗暴的关闭过程,尝试取消所有运行中的任务,并且不再启动队列中尚未开始启动的任务。
awaitTermination: 这个方法有两个参数,一个是timeout即超时时间,另一个是unit即时间单位。这个方法会使线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。
- ExecutorService service = Executors. newFixedThreadPool(3);
- for ( int i = 0; i < 10; i++) {
- System. out.println( "创建线程" + i);
- Runnable run = new Runnable() {
- @Override
- public void run() {
- System. out.println( "启动线程");
- }
- };
- // 在未来某个时间执行给定的命令
- service.execute(run);
- }
- // 关闭启动线程
- service.shutdown();
- // 每隔1秒监测一次ExecutorService的关闭情况.
- service.awaitTermination(1, TimeUnit. SECONDS);
- System. out.println( "all thread complete");
- System. out.println(service.isTerminated());
访问共享对象可以使用的机制有synchronized,volatile,ReentrantLock。
有了synchronized 为啥JSR 166 小组花了这么多时间来开发 java.util.concurrent.lock 框架呢?答案很简单-同步是不错,但它并不完美。它有一些功能性的限制 —— 它无法中断一个正在等候获得锁的线程,也无法通过投票得到锁,如果不想等下去,也就没法得到锁。同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况。
Lock 和ReentrantLock
Lock接口中定义了一组抽象的加锁操作。Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁后去操作。ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。在确实需要一些 synchronized 所没有的特性的时候,比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。
格式:
Lock lock = new ReentrantLock();
lock.lock();
try {
// update object state
}
finally {
lock.unlock();
}
必须在finally 中来释放Lock。
定时锁和轮询锁是为了避免死锁的发生。如果不能获取所需要的锁,可以使用定时的或者轮询的锁获取方式,从而使你重新获的控制权。
(1)、轮询锁( tryLock())
(2)、定时锁(tryLock(timeout, NANOSECONDS))。如果操作不能在指定的时间内给出结果,那么就会使程序提前结束
(3)、定时以及可中断的锁(lockInterruptibly)
(4)、读写锁(ReadWriteLock)。可以被多个读者访问或者被一个写者访问。
ReentrantLock 每次只能一个线程访问加锁的数据,从而达到维护数据完整性的目的。通过这种策略可以避免写/写和写/读冲突。但是也同时避免了读/读冲突。但是有些时候读操
作是可以被并发进行的。所以需要读写锁。
eg:实现一个简单的缓存。
现在的处理器(包括 Intel 和 Sparc 处理器)使用的最通用的方法是实现名为“比较并交换(Compare And Swap)”或 CAS 的原语。
- public class CASCount implements Runnable {
- private SimilatedCAS counter = new SimilatedCAS();
- @Override
- public void run() {
- for (int i = 0; i < 10000; i++) {
- System.out.println(this.increment());
- }
- }
- public int increment() {
- int oldValue = counter.getValue();
- int newValue = oldValue + 1;
- while (!counter.compareAndSwap(oldValue, newValue)) { //如果CAS失败,就去拿新值继续执行CAS
- oldValue = counter.getValue();
- newValue = oldValue + 1;
- }
- return newValue;
- }
- public static void main(String[] args) {
- Runnable run = new CASCount();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- }
- }
- class SimilatedCAS {
- private int value;
- public int getValue() {
- return value;
- }
- // 这里只能用synchronized了,毕竟无法调用操作系统的CAS
- public synchronized boolean compareAndSwap(int expectedValue, int newValue) {
- if (value == expectedValue) {
- value = newValue;
- return true;
- }
- return false;
- }
- }
JDK 5.0引入底层CAS支持,java.util.concurrenent.atomic.AtomicXXX,使用底层的JVM支持为数字和引用类型提供一种高效的CAS操作。
eg:AtomicInteger,AtomicIntegerArray,AtomicLong,AtomicLongArray
原子变量能够支持原子的有条件的,读-改-写操作。
eg:使用原子变量类 实现一个计数器。
- public class AtomicCounter implements Runnable{
- //AtomicInteger采用了系统的CAS
- private AtomicInteger value = new AtomicInteger();
- @Override
- public void run() {
- for (int i = 0; i < 10000; i++) {
- System.out.println(value.incrementAndGet());
- }
- }
- public static void main(String[] args) {
- Runnable run = new AtomicCounter();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- new Thread(run).start();
- }
- }
- public class ConcurrentStack {
- class Node {
- public final int item;
- public Node next;
- public Node(int item) {
- this.item = item;
- }
- }
- private AtomicReference<Node> top = new AtomicReference<ConcurrentStack.Node>();
- //入栈
- public void push(int item) {
- Node newNode = new Node(item);
- Node oldHead;
- do {
- oldHead = top.get();
- newNode.next = oldHead;
- } while (!top.compareAndSet(oldHead, newNode));
- }
- //出栈
- public int pop() {
- Node oldHead;
- Node newHead;
- do {
- oldHead = top.get();
- if(oldHead == null ) {
- return -1;
- }
- newHead = oldHead.next;
- //Atomically sets the value to the given updated value if the current value {@code ==} the expected value.
- } while (!top.compareAndSet(oldHead, newHead));
- return oldHead.item;
- }
- }
《Java并发编程实战》读书笔记的更多相关文章
- csapp读书笔记-并发编程
这是基础,理解不能有偏差 如果线程/进程的逻辑控制流在时间上重叠,那么就是并发的.我们可以将并发看成是一种os内核用来运行多个应用程序的实例,但是并发不仅在内核,在应用程序中的角色也很重要. 在应用级 ...
- CSAPP 读书笔记 - 2.31练习题
根据等式(2-14) 假如w = 4 数值范围在-8 ~ 7之间 2^w = 16 x = 5, y = 4的情况下面 x + y = 9 >=2 ^(w-1) 属于第一种情况 sum = x ...
- CSAPP读书笔记--第八章 异常控制流
第八章 异常控制流 2017-11-14 概述 控制转移序列叫做控制流.目前为止,我们学过两种改变控制流的方式: 1)跳转和分支: 2)调用和返回. 但是上面的方法只能控制程序本身,发生以下系统状态的 ...
- CSAPP 并发编程读书笔记
CSAPP 并发编程笔记 并发和并行 并发:Concurrency,只要时间上重叠就算并发,可以是单处理器交替处理 并行:Parallel,属于并发的一种特殊情况(真子集),多核/多 CPU 同时处理 ...
- 读书笔记汇总 - SQL必知必会(第4版)
本系列记录并分享学习SQL的过程,主要内容为SQL的基础概念及练习过程. 书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL i ...
- 读书笔记--SQL必知必会18--视图
读书笔记--SQL必知必会18--视图 18.1 视图 视图是虚拟的表,只包含使用时动态检索数据的查询. 也就是说作为视图,它不包含任何列和数据,包含的是一个查询. 18.1.1 为什么使用视图 重用 ...
- 《C#本质论》读书笔记(18)多线程处理
.NET Framework 4.0 看(本质论第3版) .NET Framework 4.5 看(本质论第4版) .NET 4.0为多线程引入了两组新API:TPL(Task Parallel Li ...
- C#温故知新:《C#图解教程》读书笔记系列
一.此书到底何方神圣? 本书是广受赞誉C#图解教程的最新版本.作者在本书中创造了一种全新的可视化叙述方式,以图文并茂的形式.朴实简洁的文字,并辅之以大量表格和代码示例,全面.直观地阐述了C#语言的各种 ...
- C#刨根究底:《你必须知道的.NET》读书笔记系列
一.此书到底何方神圣? <你必须知道的.NET>来自于微软MVP—王涛(网名:AnyTao,博客园大牛之一,其博客地址为:http://anytao.cnblogs.com/)的最新技术心 ...
- Web高级征程:《大型网站技术架构》读书笔记系列
一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计 ...
随机推荐
- Unity3D着色器Shader编程入门(一)
自学Unity3D也有大半年了,对Shader一直不敢入坑,最近看了些资料,以及通过自己的实践,对Shader还是有一点了解了,分享下仅作入门参考. 因Shader是对图像图像渲染的,学习前可以去了解 ...
- 条件查询,有input和select框,当查询条件获取焦点时支持摁下enter键查询
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...
- html第二天CSS
- Careercup | Chapter 8
8.2 Imagine you have a call center with three levels of employees: respondent, manager, and director ...
- requirejs解决异步模块加载方案
他首先会遍历enableRegistry取出其中定义的模块,并且将没有加载成功的模块标识注入noLoads数组,如果过期了这里就会报错 如果上述没问题还会做循环依赖的判断,主要逻辑在breakCycl ...
- iTop各数据表联系图(持续更新中)
- Yii2 发送邮件
http://www.cnblogs.com/wwolf/p/5438691.html?utm_source=tuicool&utm_medium=referral
- NPOI简单操作excel
本文仅当是个记录文件,仅供初学者参考. 首先得using几个npoi的空间名如下: using NPOI.HSSF.UserModel;using NPOI.HSSF.Util;using NPOI. ...
- [archlinux][hardware] 查看SSD的使用寿命
因为最近把16GB的SSD做成了HDD的cache,所以比较关系寿命问题. 使用smartctl工具. 参考:https://www.v2ex.com/t/261373 linux 下面只有 smar ...
- android MotionEvent获得当前位置
event.getY()与event.getX()为当前触摸位置距离当前元素顶端和左端的距离: event.getRowY()与event.getRowX()为当前触摸位置距离当前屏幕顶端和左端的距离 ...