《实战Java高并发程序设计》知识点

第一章 走入并行世界

概念

1. 同步/异步:同步方法调用者需等待方法调用完成后,才能执行后续行为;异步反之
2. 并发/并行:并发偏重于多个任务的交替执行,而多个任务可能是串行的;并行是真正意义上多个任务的“同时执行”
3. 临界区:公共资源/共享资源
4. 阻塞和非阻塞:当前线程被其他线程占用临界区资源而等待
5. 死锁
6. 饥饿:线程无法获得资源,导致一直无法执行
7. 活锁:相互“谦让”,共享资源在线程间跳动,没有一个线程同时拿到所有资源而执行
8. 【并发级别】★(5个):
	* 阻塞(Blocking):有序进行
	* 无饥饿(Starvation-Free):线程调度倾向于高优先级线程 (公平与非公平锁)
	* 无障碍(Obstruction-Free):(最弱的非阻塞调度)两个线程不会因为临界区导致挂起,都去操作临界区资源(乐观锁与悲观锁)
	* 无锁(Lock-Free):无锁并行都是无障碍的。无锁情况下,所有线程都可访问临界区,不同的是,无锁的并发保证必然有一个线程能在有限步完成操作离开临界区
	* 无等待(Wait-Free):无锁只要求一个线程在有限步完成,无等待要求所有线程必须在有限步内完成,这样不会引起饥饿问题

图解:

9.Java内存模型:JMM
特点:多线程的原子性、可见性和有序性
       原子性:一个操作不可中断,即使在多个线程一起执行的时候,一个操作一旦开始就不会被其他线程干扰;
       可见性:当一个线程修改了某一个共享变量值,其他线程是否能够立即知道这个修改;
       有序性:指令执行顺序

指令重排可提高CPU执行效率,有些不满足指令重排规则(Happen-Before原则)
       程序顺序原则:一个线程内保证语义的串行性
       volatile原则:volatile变量写在读之前,也保证了volatile变量的可见性
       锁规则:解锁必然发生在加锁前
       传递性:A先于B,B先于C,那么A先于C
       线程的start()方法先于它的每一个动作
       线程的所有操作先于线程的终结(Thread.join())
       线程中断(interrupt())先于被中断线程的代码
       对象的构造函数执行、结束先于finalize()方法

第二章 Java并行程序基础

线程状态Enum:
public enum State {
   	NEW,
   	RUNNABLE,
   	BLOCKED,
   	WAITTING,
   	TIMED_WAITTING,
   	TERMINATED;
}

基本操作

1.新建
通过继承Thread类本身
通过实现Runnable接口
通过Callable和Future创建线程

2.终止
不推荐使用 stop() 方法
推荐使用标志位 flag 校验结束线程

public class CreateThread extends Thread{
    CreateThread u = new CreateThread();
    volatile boolean stopme = false;
    
    public void stopMe() {
        stopme = true;
    }

    @Override
    public void run() {
        while(true) {
            if (stopme == true) {
                System.out.println("stop!");
                break;
            }
            synchronized (u) {
                //业务逻辑代码
            }
        }
    }
}

3.中断

public void Thread.interrupt() //中断线程
public boolean Thread.isInterrupted() //判断是否被中断
public static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态

可以通过判断当前线程是否被中断来做处理,类似于stopMe标志位,但是在wait() 和 sleep() 这些操作中,就只能用中断标志来完成
【注意】Thread.sleep() 方法 + 中断会触发 InterruptedExecption 异常,抛完异常并会清除中断标记

4.wait和notify
Object.notify 和 notifyAll 方法区别

Object.wait() 方法需要包含在synchronize 语句中

wait和sleep区别:wait方法会释放对象锁,sleep不会

5.挂起(suspend)和继续执行(resume) (废弃)
6.等待结束(join)和谦让(yield)
在这里插入图片描述
主线程Main会等待at线程执行完毕,不加join的话 i 不会打印到10000000;

join的核心代码:思想就是让调用线程wait在当前线程对象实例上等着

while(isAlive) {
	wait(0);
}
//yield() 是个静态方法,一旦执行,会使当前线程让出CPU,让出CPU不代表当前线程不执行。让完还会进行CPU资源争夺
public static native void yield(); 

volatile关键字

不保证原子性
在这里插入图片描述
第6行是原子性,然而结果总是小于100000!

守护线程(Daemon)

哪些是守护线程?垃圾回收线程,JIT线程

线程优先级

等级:1-10

synchronize 关键字

* 指定加锁对象:对给定对象加锁,进入同步代码前获得给定对象的锁;
* 作用于实例方法:相当于对当前实例加锁,进入同步块前要获得当前实例的锁;
* 作用于静态方法:相当于对当前类加锁,进入同步代码块前要获得当前类的锁;
//加锁对象
class SynClass implements Runnable {
    SynClass syncSlass = new SynClass();
    static int i = 0;
    @Override
    public void run() {
        synchronized (syncSlass) {
            i++;
        }
    }
}

//加锁方法
class SynClass implements Runnable {
    SynClass syncSlass = new SynClass();
    static int i = 0;
    
    public synchronized void increase() {
        i++;
    }
    @Override
    public void run() {
        increase();
    }
}

并发环境下的常见问题?
       ArrayList会出现扩容异常
       HashMap扩容链结构发生改变,成环遍历的话导致程序一直执行 【可通过jps和jstack打印排查】

第三章 JDK并发包(JUC)

重入锁 ReentrantLock

在这里插入图片描述
作用:
1.中断响应:请求锁的时候要么获得锁执行,要么等待;中断响应可以自己根据需要取消请求;
在这里插入图片描述
2.锁申请等待延时:限时等待,避免死锁
在这里插入图片描述
3.公平锁

public ReentrantLock(boolean fair); //默认是非公平锁 = false

在这里插入图片描述

Condition条件

与ReentrantLock结合使用,类似于interrupt 和 signal 方法

信号量(Semaphore)

同时允许有限个线程同时访问同一个临界区
在这里插入图片描述

ReadWriteLock 读写锁

private static ReantrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock .readLock();
private static Lock writeLock = readWriteLock.writeLock();

CountDownLatch

在这里插入图片描述

public class CountDownLatchDemo implements Runnable {
    static final CountDownLatch end = new CountDownLatch(10);//计数数量10个
    static final CountDownLatchDemo demo = new CountDownLatchDemo();

    @SneakyThrows
    @Override
    public void run() {
        Thread.sleep(new Random().nextInt(10) * 1000);
        System.out.println("check complete!");
        end.countDown();//倒计时器减一
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            executorService.submit(demo);
        }
        end.await();//要求主线程等待10个检查任务全部完成之后,主线程才能继续执行
        System.out.println("Fire");
        executorService.shutdown();
    }
}

CyclicBarrier

在这里插入图片描述
用例(待补充)

线程阻塞工具类 LockSupport(了解)

线程池

在这里插入图片描述

线程池种类与区别

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

	//1.固定大小,每批次线程执行个数固定为5个
    final ExecutorService executorService = Executors.newFixedThreadPool(5);
    //2.计划任务,返回一个ScheduledExecutorService对象,起到计划任务作用;
    //  可以在指定时间,对任务进行调度;
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
    // 2.1 任务执行5秒,调度周期为2秒。也就是每2秒,任务执行一次,一次执行5秒;
    scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
        @SneakyThrows
        @Override
        public void run() {
            Thread.sleep(5000);
        }
    },0,2, TimeUnit.SECONDS);
    // 2.2 任务执行5秒,每隔任务之间间隔等待2秒
    scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
        @SneakyThrows
        @Override
        public void run() {
            Thread.sleep(5000);
        }
    },0,2,TimeUnit.SECONDS);

线程池内部实现

不管是哪种线程池,内部都是由 ThreadPoolExecutor 类实现的;
在这里插入图片描述
在这里插入图片描述
重点掌握其中的workQueue和handler两项:

workQueue分类:
1.直接提交队列:SynchronousQueue
2.有界任务队列:ArrayBlockingQueue
3.无界任务队列:LinkedBlockingQueue
4.优先任务队列:PriorityBlockingQueue

核心调度源码实现

在这里插入图片描述

拒绝策略

在这里插入图片描述

自定义拒绝策略

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

自定义线程池创建(ThreadFactory)

在这里插入图片描述

拓展线程池

在这里插入图片描述
**【调优】**线程池大小设置多少合适?
在这里插入图片描述

Fork/Join 框架

在这里插入图片描述
MapReduce原理:归并思想

工具类(并发集合)源码实现

ConcurrentHashMap:1.7/1.8优化★
CopyOnWriteArrayList:修改时修改的是副本
ConcurrentLinkedQueue:offer方法使用CAS,实现过程★
BlockingQueue:数据共享通道,put和take方法的交互通过Condition进行交互★
ConcurentSkipListMap;跳表结构★

跳表:类似于平衡树,可以实现快速查找;相较于平衡树而言,平衡树在插入和删除元素会触发全局调整,而跳表只需要部分锁即可,高并发下性能好;
在这里插入图片描述

【结构】最底层是链表所有元素,所有元素是排序的,上一层是下一层子集,从上一层向下一层去找插入位置,最终落地至最后一层即可;
【思想】空间换时间,如下是找7的过程:
【比较】与HashMap不同,跳表的输出是有序的;

在这里插入图片描述

第四章 锁优化与注意事项

锁性能优化点

1.减少锁持有时间:只锁住共享的最小范围的方法即可;示例:JDK中Pattern类中实现
2.减小锁粒度:ConcurrentHashMap的put和get方法
3.读写分离锁替换占用锁
4.锁分离
5.锁粗化:减少不必要的细粒度的锁

JVM锁优化

1.锁偏向
在这里插入图片描述

2.轻量级锁
在这里插入图片描述

3.自旋锁
在这里插入图片描述

4.锁消除

锁消除是一种更彻底的锁优化。JVM在JIT编译时,通过对上下文的扫描,去除不可能存在共享资源竞争的锁,通过锁消除,可以节省毫无意义的请求锁时间;

ThreadLocal 线程局部变量

1.使用:简单容器作用
在这里插入图片描述
在这里插入图片描述
2.核心源码实现:set和get方法
在这里插入图片描述
在这里插入图片描述
3.清理回收过程
值的存取是在线程的ThreadLocalMap中,维护也是在Thread类内部的,意味着线程不退出,对象引用一直存在;
证明:在Thread类清理工作中,就包含了对ThreadLocalMap的清理
在这里插入图片描述在这里插入图片描述
ThreadLocal缺点:不适合用于存大对象,会造成内存泄漏,应为不用的时候不会被自动清理;对象不用可以使用ThreadLocal.remove()方法移除对象;

在这里插入图片描述

无锁 CAS

CAS 算法过程:CAS(V,E,N),V-要更新的变量;E-预期值;N-新值;
只有当V=E的时候,才会把V设为N;

AutomicInteger

在这里插入图片描述
CAS 核心源码实现:在这里插入图片描述

拓展:
Java中指针:Unsafe类(了解)
无锁对象引用:AutomicReference(了解)
带时间戳的对象引用:AtomicStampedReference(了解)
无锁数组:AutomicIntegerArray(了解)

死锁

第五章 并行模式与算法

单例模式(四种实现)

1.懒汉式
2.饿汉式
3.静态内部类
4.双重校验锁

生产者-消费者模式

在这里插入图片描述
使用BlockingQueue实现生产者-消费者模型是利用锁和阻塞等待实现的线程之间的同步;
替代实现为ConcurrentLinkedQueue,大量使用CAS操作;

Future模式

异步调用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

并行排序算法

NIO

利用NIO改造Socket实现通信

第六章 Java8与并发

Scalar(了解)
函数式编程
原子类增强 LongAdder: 原理就是将内部核心数据 value 分离为一个数组,每个线程访问时,通过哈希等算法映射到某一个数字进行计数,得到最终结果

第七章 使用Akka构建高并发程序

Akka模式

第八章 并行程序调试(略)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wimb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值