Java并发编程从入门到精通 张振华 清华大学出版社 ISBN-9787302401919
仅供参考, 自建索引, 以备后查
=================================================================
一:
CPU型号
# cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
CPU核心数
# cat /proc/cpuinfo | grep 'core id' | sort -u | wc -l
CPU线程数
# cat /proc/cpuinfo | grep 'processor' | sort -u | wc -l
进程 线程
箱子 箱子里面的球
PCB TCB
并行: 线程小于CPU支持的最大数量, 线程同时运行
并发: 线程大于CPU支持的最大数量, 时间片轮转进程调度, 线程轮番执行
当线程数量大数CPU支持的最大数量时, 并行->并发
分布式: 多台机器分别计算, 最后结果合并
多线程, 高性能Web前端调优, 代码模块化, 异步化
线程安全, 线程死锁, 线程过多切换过头, 线程池
=================================================================
二: Java Thread
线程实现
class A extends Thread
new A().start();
class B implements Runnable
new Thread(new B()).start();
class C implements Callable<String>
new Thread(new FutureTask<String>(new C())).start();
优先级: MIN_PRIORITY=1 NORM_PRIORITY=5 MAX_PRIORITY=10
生命周期: new runnable running blocked dead
Thread.currentThread().getThreadGroup();
ThreadLocal 拷贝创建当前线程数据副本
内部使用 ThreadLocalMap<Thread, V> 线程对象作Key,线程安全
unchecked exception 可以通过 setUncaughtExceptionHandler(..) 捕获
=================================================================
三: Thread安全
CPU 寄存器->高速缓存->内存
原始数据在内存 计算时拷贝到 寄存器或高速缓存 之后写回内存
对于共享变量就存在了多线程并发问题
线程不安全: 仅仅只是针对多个线程访问同一个变量才会有机会发生
如: 访问单例对象, 未使用锁, 未使用线程安全的类库
线程之间没有交互,没有共享变量,就不会发生,即线程安全
对象方法锁 F
public synchronized void method1(..) {}
代码块锁 D
public void method2(..) { synchronized(obj) {} }
变量高效锁 B
private byte[] lock3 = new byte[1];
public void method3(..){ synchronized(lock3) {} }
显示锁
public interface Lock {}
public class ReentrantLock implements Lock {}
class C {
private final Lock lock = new ReentrantLock();
private void methodC(Object... args) {
lock.lock();
try {
// TODO
}
finally {
lock.unlock();
}
}
}
读写锁
public interface ReadWriteLock {}
public class ReentrantReadWriteLock implements ReadWriteLock {}
ReadWriteLock: 读读不互斥, 读写互斥, 写写互斥, 高并发时效率超高
public class StampedLock {}
支持乐观悲观方式读写 JDK1.8
volatile [ˈvɒlətaɪl]
public volatile static int count = 0;
java.util.concurrent.atomic 原子操作
AtomicInteger, AtomicLong, AtomicBoolean
AtomicReference, AtomicIntegerArray, AtomicReferenceArray
AtomicIntegerFieldUpdater, AtomicReferenceFieldUpdater
Java 中 atomic 原子操作是利用 CPU 比较并交换 CAS: Compare and Swap
使用 JNI: Java Native Interface 调用 C 执行 CPU 底层指令
单例模式线程安全性能高的两种写法
class One {
private static One one;
private static byte[] lock = new byte[1];
private One() {}
public static One getInstance() {
if (one == null) {
synchronized(lock) {
if (one == null) one = new One();
}
}
return one;
}
}
class Two {
private static Two two;
private static ReentrantLock lock = new ReentrantLock();
private Two() {}
public static Two getInstance() {
if (two == null) {
lock.lock();
if (two == null) two = new Two();
lock.unlock();
}
return two;
}
}
=================================================================
四: 线程安全的集合类
Collection—>Set/List/Queue
Map—>ConcurrentMap/SortedMap
Hashtable
public synchronized .. ..(..) {}
ConcurrentHashMap 内部使用 ReentrantLock
CopyOnWriteArrayList 内部使用 ReentrantLock
线程安全,读操作远远大于写操作时性能超级高,高并发性能强,不受多线程并发问题影响
CopyOnWriteArraySet 使用 CopyOnWriteArrayList
CopyOnWrite 任意读,修改时读出数据到新对象,修改数据再加锁使原对象指向新对象
StringBuffer 线程安全, 使用synchronized加锁
StringBuilder 线程不安全效率高
=================================================================
五: 多线程交互
Queue 队列 FIFO
Deque 双端队列
BlockingQueue 阻塞队列 add/offer/put remove/poll/take element/peek
ArrayBlockingQueue
LinkedBlockingQueue 生产锁 消费锁 高并发性能高
PriorityBlockingQueue 不阻塞生产者
DelayQueue 可用于去除到期数据, 或者任务调试 使用时需要实现 Delayed 接口
SynchronousQueue 生产消费一对一传递,吞吐量高于 [Linked/Array]BlockingQueue
LinkedBlockingDeque 链表双向阻塞队列
LinkedTransferQueue 生产的对象可能不入列就交给消费者了,效率更高,具有异步特性
AbstractQueuedSynchronized 抽象对列同步器
CountDownLatch 同步计数器
void countDown(); 计数减一
void await(); 阻塞到计数为零
例: 当前线程启动N个子线程后开始等待, 等到所有子线程执行完毕再开始执行
注: 所有子线程必须拥有的相同的CountDownLatch对象
注: 设定数量必须等于开始的线程数量
Semaphore [ˈseməfɔ:(r)] 同步计数器
void acquire(); 获取信号开始阻塞
void release(); 释放信号开始执行
例: 有100个文件需要上传,使用Semaphore设置信号量为3,最大同时允许3个线程支行
注: 及时调用release()释放,不然会一直等待
注: 所有子线程必须拥有的相同的Semaphore对象
CyclicBarrier [ˈsaɪklɪk] [ˈbæriə(r)] 同步计数器
int getParties(); 阻塞线程的计数器
int await(); 线程阻塞,直到调用await()次数==getParties()返回值,所有线程向下执行
例: 评委们先后确定最终投票人,但是必须等到最后一名评委确定人选都能开始统计
注: 所有子线程必须拥有的相同的CyclicBarrier对象
注: await()调用次数必须等于getParites()返回值
=================================================================
六: 线程池
Executor—>ExecutorService
Executors
newSingleThreadExecutor LinkedBlockingQueue
newFixedThreadPool LinkedBlockingQueue
newCachedThreadPool SynchronousQueue
newWorkStealingPool ForkJoinPool
线程完成任务时间可分为
T1: 创建线程消耗
T2: 任务执行
T3: 销毁线程
其中T2的执行时间与业务相关,相对固定. 线程池在T1与T3阶段优化执行效率
工作原理:
核心队列: 等待执行队列 BlockingQueue 任务执行队列: HashSet<Worker>
corePoolSize: 线程池中最少线程数量 maximumPoolSize: 最大处理线程数
public interface ThreadFactory {} 线程工厂
public interface RejectedExecutionHandler {} 线程饱和对列已满后的丢弃处理
ThreadPoolExecutor 线程池定义 <<<<=========
AbortPolicy 直接抛出RejectedExecutionException异常
CallerRunsPolicy 回到调用线程的父线程执行,要是父线程已死则丢弃
DiscardOldestPolicy 丢弃最早入列未执行的线程
DiscardPolicy 丢弃
ExecutorService es = Executors.newCachedThreadPool();
Stream.iterate(1, s->s+1).limit(100).forEach(i->{
es.execute(()->{
System.out.println("Thread-"+i + "--------");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread-"+i + "<<<<<<<<");
});
});
es.shutdown();
/// 如果需要等待线程池所有任务执行完毕再往下执行
while ( ! es.isTerminated()) {
}
注意事项:
线程池使用单例, 不用单例模式就是创建N个线程池了不是吗??
线程池数量大小
线程中注意别出现死锁代码
=================================================================
七: Fork/Join
接口
public interface Runnable {}
public interface Future<V> {}
public interface Callable<V> {}
public interface RunnableFuture<V> extends Runnable, Future<V> {}
实现类
public class FutureTask<V> implements RunnableFuture<V> { }
使用方法
new Thread(new FutureTask<String>(new Callable<String>())).start();
调用 Future get() 方法会阻塞线程等待执行完毕返回结果
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableRunnableFutureTaskDemo1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
/// Callable 实例
LongTimeWorkCallable cc = new LongTimeWorkCallable();
FutureTask<String> f1 = new FutureTask<String>(cc);
/// Runnable 实例
DaemonWorkRunnable dd = new DaemonWorkRunnable();
FutureTask<Integer> f2 = new FutureTask<Integer>(dd, 111);
/// 开始执行
new Thread(f1).start();
new Thread(f2).start();
/// 结果获取
System.out.println(f1.get());
System.out.println(f2.get()); /// 返回值为 111, 调用时传递的预定义返回结果
}
}
class DaemonWorkRunnable implements Runnable {
public void run() {
System.out.println("----Deamon work launchs");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----Deamon work accomplishes");
}
}
class LongTimeWorkCallable implements Callable<String> {
public String call() throws InterruptedException {
System.out.println("-----Work starts...");
Thread.sleep(5000);
System.out.println("-----Work completes");
return "Work-100%";
}
}
执行后的控制台显示内容, 两个线程开始执行, 但是调用 get 方法有先后, 若先调用的线程执行时间较长, 则会阻塞到这一线程执行完毕
-----Work starts...
----Deamon work launchs
----Deamon work accomplishes
-----Work completes
Work-100%
111
若把上面的打印语句的顺序调换一下
/// 结果获取
System.out.println(f2.get()); /// 返回值为 111, 调用时传递的预定义返回结果
System.out.println(f1.get());
则控制台会打印, 因为 f2 执行消耗时间短, 所以会先打印, 再等待 f1 执行完成
-----Work starts...
----Deamon work launchs
----Deamon work accomplishes
111
-----Work completes
Work-100%
Fork/Join框架 Map/Reduce框架
fork() 执行切割后的数据运算
join() 对fork()结果进行合并
map() 对Datanode节点的数据进行运算之后Combine
reduce() 对map()结果进行合并
public abstract class ForkJoinTask<V> implements Future<V>, Serializable {}
public abstract class RecursiveAction extends ForkJoinTask<Void> {} 无返回结果
public abstract class RecursiveTask<V> extends ForkJoinTask<V> {} 有返回值
执行 Fork/Join 任务需要 ForkJoinPool 线程池
public abstract class AbstractExecutorService implements ExecutorService {}
public class ForkJoinPool extends AbstractExecutorService {}
public void execute(..) {} 异步执行
public ForkJoinTask<?> submit(..) {} 返回Future对象
继承 RecursiveTask / RecursiveAction 并实现 protected abstract V compute(); 进行数据切割
work-stealing 工作窃取算法
ForkJoinTask public final boolean isCompletedAbnormally() {} 线程执行中是否出现异常
=================================================================
八: WEB项目线程线程池的应用
Servlet
void init(ServletConfig config);
void service(ServletRequest req, ServletResponse rep);
void destroy();
Tomcat 可选 线程池 在 server.xml 修改
<Executor name="executor1Name" .../>
<Connector executor="executor1Name" .../>
public abstract interface org.apache.catalina.Executor extends java.util.concurrent.Executor, org.apache.catalina.Lifecycle {}
实现 Executor 可以自定义可在 Tomcat 中使用的线程池
Nginx线程池相关介绍
数据库连接池介绍
com.alibaba.druid.pool.DruidDataSource
CountDownLatch, AtomicLong, ReentrantLock, volatile
如何在分布式系统中实现高并发: 了解机器 业务 程序 开始拆分
- 任务性质: CPU计算型任务, IO密集型任务, 内存消耗型
- 任务并发数量: 超大并发, 大并发, 中等, 很少
- 任务执行时间: 长耗时, 中等, 很少
- 任务优先级: 高, 中, 低
- 任务依赖性: 是否依赖数据库连接, 业务之间是否相互调用依赖
- 资源性质: 静态资源, 动态资源
- 业务是否耦合: 高耦合, 可解耦, 无耦合
部署分离:
- 渠道分享: 后端, 前端, APP端, API接口
- 运营商: 电信, 联通, 教育, 海外服务
- 服务内容: 文字, 图片, 视频, 下载服务
- 访问密集性: 高并发, 低并发分别布置
=================================================================
九: 线程监控
public class ThreadPoolExecutor extends AbstractExecutorService {
protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
}
继承 ThreadPoolExecutor 类, 重写以上三个方法, 可以在线程执行前, 执行后和线程池关闭后
taskCount completedTaskCount largestPoolSize getPoolSize getActiveCount
ForkJoinPool 监控通过拥有的方法进行查阅
Java内存结构
GC: Garbage Collector
JVM: 程序计数器 JVM栈 本地方法栈 共享堆 方法区
程序计数器: 当前线程执行字节码的行号指示器. 线程私有内存, 唯一一个没有内存溢出的区域
JVM栈: 线程私有, 方法执行局部变量表, 操作数栈, 动态链接和方法出口信息. 方法入栈出栈
本地方法栈: Java调用操作系统本地方法服务
共享堆: 所有线程共享内存区域, 为了有些话对象实例. 新生代和年老代
方法区: 所有线程共享, 存储被虚拟机加载的 类信息, 常量, 静态变量和即时编译后的代码
垃圾回收机制:
Permanent Generation: 对应方法区, 几乎不参与垃圾回收
Young Generation: Eden(minorGC), Survior, Survior
Old Generation: MajorGC
FullGC 对整个堆内存进行垃圾回收
串行收集器: 单线程垃圾回收
并行收集器: 对年轻代进行垃圾回收, 多线程多处理器上使用
并发收集器: 适用大中高型大规模应用
内存溢出:
JVM Heap堆内存溢出 -Xmn -Xms -Xmx
PermGen space内存溢出 存放 Class 对象及 Meta 信息 -XX:PermSize -XX:MaxPermSize
java.lang.StackOverflowError 栈溢出 -Xss 设置线程内存大小
Server JVM参数设置
JAVA_OPTS="-Xms4g -Xms4g -Xmn1024m -XX:PermSize=512m -XX:MaxPermSize=512m -XX:SurvivorRatio=6"
Java运行状况可视化监控工具
VisualVM
VisualVM+JTA插件
JConsole
Oracle Java Mission Control jmc.exe
Linux线程分析监控
top
JAVA_HOME/bin/jstack
Eclipse Window->Preferences Java-Debug-Default suspend policy for new breakpoints: Suspend VM
压力测试 Badboy+JMeter
MultithreadedTC 并发测试
=================================================================
十: Android线程
<activity/> <service/> <receiver/> <provider/> 可以指定 android:process 指定支行的线程
<Application/> 也支持 android:process
Android 进程重要性等级
- 前台进程 顶级优先重要
- 可见进程
- 服务进程
- 后台进程
- 空进程 最先回收
若前台进程或可见进程包含服务进程, 则此服务进程优先级升级到前台进程或可见进程
Activity生命周期 onCreate() onStart() onResume() onPause() onStop() onDestroy()
UI线程阻塞会出现 应用程序无响应ANR 对话框
非UI线程更新界面
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handler
Android异步线程
public abstract class AsyncTask<Params, Progress, Result> {}
实现 doInBackground() 若有返回结果则需要再 onPostExecute() 方法更新UI界面
在以上两方法中可以随时 publishProgress() 更新处理进度
onPreExecute() onPostExecute() onProgressUpdate()