《实战Java高并发程序》_笔记

1,走进并行世界

1.2 概念

同步,异步;被调用的方法是否本线程执行
串行,并行/并发;时间顺序,时间片
临界区;共享资源
阻塞,非阻塞;
死锁(相互持有资源),饥饿(无法抢占资源),活锁(冲突时始终释放资源);

1.3 并发级别

阻塞,无饥饿(公平锁);
无障碍(标志位判定),无锁(cas),无等待(CopyOnWrite)。

1.4 两个定律

加速比:R=T1/Tn;T1为单处理器耗时,Tn为多处理器耗时。
Admal:R=T1/T1(F+(1-F)/n)=1/(F+(1-F)/n);F为串行代码比例
Gustfason:R=(a+nb)/(a+b)=n-F(1-n);
Admal分析单个任务,Gustfason分析多个任务。响应速度,吞吐量。

1.5 JMM特性

原子性;32位JVM的long操作,不是原子性操作。
可见性;操作java栈的数据,不会实时写到堆中。
有序性;JVM指令重排,减少CPU流水线的中断。可保证串行语义一致,不保证并行语义一致。
锁特性:阻塞、排他、可重入
CPU执行指令
取指,IF;
译码,ID;
执行,EX;
访问存储器,MEM;
写回,WEB;
每个步骤访问不同硬件;重排指令,避免中断,保证硬件连续性工作,提高效率。
Happen-Before
程序顺序原则;先声明/创建,后使用
volatile禁止重排。
锁规则,先lock()后unLock()
线程规则,start()最先,interrrupt()先于中断检测,线程A中调用ThreadB.join()时B先于A返回。
保证执行逻辑的情况下,重排指令。

2,Java并行程序基础

线程状态:new、runnable、blocked、wating、timed_waiting、terminated

2.2 线程的基本操作

Thread#start();启动
Thread#interrupt()/isInterrupted();通知中断(非强制停止),是否中断。中断逻辑需自己处理。
Thread.interrupted();当前线程是否中断,查询后设置标志位为false。
Thread.sleep();线程休眠。被中断时抛异常,并清除中断标记。timed_waiting,不释放锁。
Object#wait()/notify();等待(必须手动唤醒,或指定延时)并释放锁,唤醒(不释放锁)。
sychronized;阻塞状态。
Thread#join();加入,先start()后join()。多个加入的线程并发执行。
Thread#yield();让步,runnable。

2.8 不可变类加锁问题

String、Integer等不可变类,修改值后对象变化;会丢失加锁信息。
故如在同步代码中修改了String、Integer值,则不应对其加锁;应加在其他对象上。
多线程间看到的不是同一对象。
synchronized锁标记,加在对象上;退出代码块时释放锁。激活其他阻塞线程,执行逻辑。此时变量值可见,但锁的排他性破坏,并发修改,可能丢更新。

3,JDK并发包

同步控制、线程池、并发容器

3.1 同步控制

资源同步(synchronized、Lock/LockSupport)、线程协调(Semaphore、CountDownLatch、CyclicBarrier)
ReentrantLock;可重入锁。中断、时限、公平。

  • lock()/unlock();加锁/阻塞,解锁
  • lockInterruptibly();加锁,中断抛异常
  • tryLock();指定时间内加锁

Condition;可重入锁的释放、通知。

  • ReentrantLock#newCondition();获取实例,一个锁可有多个Condition,相互间可通信。
  • await();释放锁。Object#wait()
  • awaitUniterruptibly();等待,不响应中断。
  • signal()/signalAll();通知,等待线程进入阻塞态。Object#notify()

Simaphore;信号量。指定多个线程访问同一资源。锁指定单线程访问同一资源。

  • aquire()/tryAquire();获取,线程中断时抛异常
  • aquireUninterruptibly();获取,不响应线程中断
  • release();释放

ReadWriteLock;读写锁。

  • readLock();共享
  • writeLock();独占

CountDownLatch;倒计数器。

  • countdown();计数减一
  • await();等待。等待指定数量的线程完成。

CyclicBarrier;循环栅栏。可设置多个阻塞点,同步线程进度。类似CountDownLatch。

  • new CyclicBarrier(N, runnable);N指定数量,runnable每次跨过屏障点时的回调。
  • await();等待,计数加一;等于指定数值时继续执行。

LockSupport;

  • LockSupport.park();阻塞,可响应中断。permit为1时不阻塞,0时阻塞。
  • LockSupport.unpark();激活任意线程。permit加1,最大为1。
  • LockSupport.park(obj);阻塞,obj用于监视、诊断。obj未加锁,可正常操作。
  • LockSupport.getBlocker();获取线程同步器

线程中断响应,响应方式(抛异常并退出阻塞、直接退出阻塞、不响应)。

  • 线程;join()、sleep()、Object#wait()、Condition#await(),抛异常。
  • synchronized;不响应中断
  • Lock、Semaphore;抛异常,或不响应。
  • CountDownLatch、CyclicBarrier;抛异常
  • LockSupport;退出park()阻塞

3.2 线程池

Executor–ExecutorService–ScheduledExecutorService、(AbstractExecutorService–ThreadPoolExecutor/ForkJoinPool);线程池继承关系
Executors工厂类
ScheduledExecutorService;调度线程池。不会堆叠任务,任务异常时取消对应任务的后续执行。

  • scheduleWithFixedDelay();固定延时,执行间隔=任务时间+delay
  • scheduleAtFixedRate();固定周期,执行间隔=max(任务时间,rate)。

ThreadPoolExecutor
线程池实现类。参数:

  • corePoolSize;核心线程数。
  • maximumPoolsize;最大线程数
  • keepAliveTime;超出corePoolSize的线程空闲时间。
  • unit;时间单位
  • workQueue;任务队列。
  • threadFactory;线程工厂,ThreadFactory实现类,默认Executors.defaultThreadFactory()。
  • handler;拒绝策略
  • 任务队列,有界、无界:
    SynchronousQueue;有界,0。
    ArrayBlockingQueue;有界,n。
    LinkedBlockingQueue;无界,Integer.MAX_VALUE。
    PriorityBlockingQueue;无界,Integer.MAX_VALUE - 8。按优先级执行。

拒绝策略RejectedExecutionHandler,线程数达到maximumPoolSize,且队列满时拒绝。

  • AbortPolicy;抛异常。默。
  • CallerRunPolicy;在调用者线程中运行任务
  • DiscardOldestPolicy;丢弃最老,并尝试重新加入队列
  • DiscardPolicy;直接丢弃

也可自己实现RejectedExecutionHandler。
扩展功能;继承ThreadPoolExecutor:

  • beforeExecute();执行前
  • afterExecute();执行后
  • terminated();退出线程池

线程数估计:
N=CPU数*CPU使用率/计算时间占空比
打印完整错误堆栈信息,继承ThreadPoolExecutor:

//execute()、submit()等方法中,传入带标记Exception的Runnable、Callable实例,
		private Runnable wrap(Runnable task,Exception logExp,String tName) {
			return new Runnable() {
				public void run() {
					try { task.run();
					} catch (Exception e) {
						logExp.printStackTrace();
						throw e; 
					} 
				} 
			}; 
		 }

ForkJoinPool
submit();提交任务,获取ForkJoinTask
ForkJoinTask,抽象类:

  • get();获取结果

抽象类:RecursiveTask返回、RecursiveAction无返回

  • compute();运算,抽象方法。通过List保存子任务,遍历获取结果集。
  • fork();派生子任务。调用compute()结果集,减少线程使用数。
  • join();获取子任务结果集。

3.3 jdk并发容器

Concurrent前缀,写;CopyOnWrite前缀,写时复制,读。
ConcurrentHashMap;并发Map。
CopyOnWriteArrayList;并发List
ConcurrentLinkedQueue;并发Queue
BlockingQueue;阻塞队列。消息队列。
ConcurrentSkipListMap;跳表。有序。
Collections包装集合。所有操作,先synchronized加锁。
Hashtable、Vector;同步方法实现线程安全。

4,锁的优化及注意事项

锁使用:悲观锁、乐观锁。
锁类型:可重入锁、读写锁、
锁实现:偏向锁、自旋锁、时限锁

4.1 建议

减少锁持有时间
减小锁粒度
读写锁替换独占锁
锁分离;对不可能冲突的代码,使用不同的锁
锁粗化;频繁获取释放锁时,考虑锁粗化。如循环中获取锁,可放到循环外。
减少串行代码、减少锁消耗、使用读写锁或不同锁避免阻塞。

4.2 JVM的锁优化

锁类型:偏向锁、轻量级锁(CAS修改对象头加锁,失败且对象不指向本线程则膨胀)、重量级锁(自旋、阻塞)
锁消除;消除不可能冲突的代码锁。基于逃逸分析。

4.3 ThreadLocal

ThreadLocal.ThreadLocalMap,存放ThreadLocal值,key为ThreadLocal实例。
Thread#threadLocals;指向本线程的ThreadLocalMap。
ThreadLocal#set()/get();设置,获取
ThreadLocal#remove();线程池中运行时手动清除。
threadLocal=null也可清除。ThreadLocalMap的Entry为WeakReference,弱引用。不持有ThreadLocal的引用。
引用类型:

  • 强引用;
  • 软引用;SoftReference,内存不足时回收。用于内存敏感需求,如cache。
  • 弱引用;WeakReference,GC时回收
  • 虚引用;PhantomReference,只用于判定是否被回收

引用队列;ReferenceQueue。

4.4 无锁

CAS ,Compare And Swap。比较交换。类似MVCC。
CAS(V, E, N),V为待更新变量,E为更新后值,N为更新值。
com.misc.Unsafe;封装的指针操作,可进行CAS操作。
Atomic族,原子操作。关键属性:value、offset(对象偏移,对象头长度)

  • Integer、Long、Reference、FieldUpdater、Array;有对应原子类
  • FieldUpdater注意点:变量可访问(反射),可见(volatile),不能修改static变量。

5,并行模式与算法

单例模式、不变模式、消费者-生产者模式、Future模式。
数据可见(同步、冲突串行)、执行互不干扰。

5.4 高性能的生产者-消费者模式

示例链接
Disruptor框架,环形队列。

  • cursor;当前位置
  • sequence;序列值,入队加1。sequence&(queueSize-1),获取元素实际位置。

核心类:Disruptor调度器、EventHandler/WorkHandler消费者、RingBuffer(环形队列,生产者将消息存入)
Disruptor绑定消费者,生产者向RingBuffer写入消息;线程安全通过CAS操作获取sequence实现。
生产者类型,ProductType枚举类。
消费者监听策略,WaitStrategy实现类:

  • BlockingWaitStrategy;阻塞
  • SleepingWaitStrategy;sleep
  • YieldingWaitStategy;让步;消费者线程数小于逻辑CPU数(CPU线程数)
  • BusySpinWaitStrategy;死循环;消费者线程数小于物理CPU数

空白填充,防止缓存失效。

6,Java8与并发

java函数式编程特性:无副作用,对象不变。
函数式接口只能有一个抽象方法;任何被Object实现的方法不被视作抽象方法。
继承多接口,有相同静态方法、默认方法时;必须重写,调用指定父类方法:InterfaceA.super.mName()
Stream类型:IntStream、LongStream、DoubleStream、Stream

6.5 CompletableFuture

实现了Future、CompletionStage;可流式调用。
默认线程池ForkJoinPool.commonPool();为deamon线程。
API,示例

  • supplyAsyc()/runAsync();静态工厂方法,
  • get()/getNow();获取结果
  • complete()/completeExceptionally();未完成时,设置返回值
  • thenApply()/thenApplyAsync();带参返回,Async指不在当前线程执行
  • thenAccept()/thenAcceptAsync();带参无返回
  • thenRun()/thenRunAsync();无参无返回
  • thenCompose()/thenCombine();协调执行,返回参数看函数要求。
  • whenComplete();完成回调

6.6 StampedLock

读写锁的改进,增加了乐观锁,比对stamp判定是否脏读。
API:

  • writeLock()/readLock();写锁,读锁
  • unLockWrite()/unLockRead();解锁
  • tryOptimisticLock()/validate();乐观锁,验证是否脏读。MVCC,写时更新stamp

阻塞使用Unsafe.park(),中断时直接返回。
StampedLock通过自旋、挂起(Unsafe.park())获取锁,挂起后中断线程,因方法中无中断处理逻辑,会一直自旋,占用大量CPU资源。

6.7 原子类的增强

LongAdder、LongAccumulator、DoubleAdder、DoubleAccumulator
分解数值为多个数值之和,Cell[];运算时,对单个Cell进行计算,避免冲突。
sum(),longValue()/doubleValue();获取值

7,Akka

核心类:UntypedActor、ActorRef、ActorSystem
Akka由消息驱动。Actor之间的消息需要满足不变性,推荐使用不可变对象。
消息投递具有顺序性;消息的可靠性应该由业务层保证。
Actor生命周期:
新建;actorOf()。回调preStart()。
重启;旧实例回调preRestart(),系统创建新实例,新实例回调postRestart()。
停止;stop(),回调postStop(),同时监视器接收到teminated消息。

8,多线程调试

断点右键,设置断点模式,断点条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值