「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」
线程和进程
线程和进程
进程:一个程序的集合
一个进程往往可以包含多个线程,至少包含一个;
线程:开了一个进程,之后多线程处理
1 | java复制代码 public synchronized void start() { |
并发和并行
并发(多线程操作同一个资源):
- 抢票
- cpu一核,模拟出来多条线程,天下武功,唯快不破,快速交替
并行(多人个一起行走):
- cpu多核,多个线程一同执行
1 | java复制代码package com.jxau.demo01; |
并发编程的本质:提高效率,充分利用CPU的资源
线程有几个状态
1 | java复制代码public enum State { |
wait/sleep的区别
Lock锁
synchronized
1 | java复制代码package com.jxau.demo01; |
lock
公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队
使用onglock的步骤:
Synchronized 和LOck 区别
1、Synchronized内置的Java关键字,Lock是一个Java类
2、Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
3、Synchronized 会自动释放锁,lock必须要手动释放锁!如果不释放锁,死锁
4、Synchronized线程1(获得锁,阻塞)、线线程2(等待,傻傻的等) ; Lock锁就不一定会等待下去;
5、Synchronized可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置) ;
6、Synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码!
生产者和消费者问题
判断等待、业务、通知唤醒
1 | java复制代码package com.jxau.pc; |
A、b、c、d四个线程可能出现的问题
==要使用while==
juc版的生产者与消费者问题
使用lock condition.await() condition.singal()
通过look找到condition
1 | java复制代码package com.jxau.pc; |
**任何一个新的技术,绝对不仅仅只是覆盖了原来的技术,优势和补充!
Condition精准的通知和唤醒线程
1 | java复制代码package com.jxau.pc; |
八锁问题
synchronized锁的对象时方法的调用者!
一个静态方法锁,一个普通方法锁
集合类不安全
hashset的底层
1 | java复制代码 public HashSet() { |
Runnable
1 | java复制代码package com.jxau.callable; |
常见的辅助类(可以解决高并发限流)
CountDownLatch
1 | java复制代码package com.jxau.count; |
原理:
countDownLatch.countDown();
数量-1
countDownLatch.await();
等待计数器归零,再往下执行
CyclicBarrier
1 | java复制代码package com.jxau.count; |
Semaphore
1 | java复制代码package com.jxau.count; |
原理:
semaphore.acquire();
获得,假设如果已经满了,进行等待,等待到被释放为止
semaphore.release();
释放,会将当前的信号量释放+1,然后唤醒等待的线程
区别与联系:
CyclicBarrier: 指定个数线程执行完毕后在执行操作
Semaphore: 同一时间只能有指定数量个得到线程
读写锁
ReadWriteLock
独占锁(写锁)’一次只能被一个线程占有
共享锁(读锁)多个线程可以同时占有
ReadwriteLock
读-读可以共存!
读-写不能共存!
写-写不能共存!
1 | java复制代码package com.jxau.rw; |
阻塞队列
什么情况下我们会使用 阻塞队列:多线程并发处理,线程池!
四组常用API
学会使用队列
添加、移除
1、抛出异常
2、不会抛出异常
3、阻塞 等待
4、超时 等待
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer(,,) |
移除 | remove() | poll() | take() | poll() |
检测队首元素 | element() | peek() | - | - |
1 | java复制代码 /* |
1 | java复制代码 /* |
1 | java复制代码 /* |
1 | java复制代码 /* |
SynchronousQueue
同步队列 和其他的BlockQueue
不一样,SynchronousQueue
不存储元素put了一个元素,必须从里面先take取出来,否则不能在put进去值!
1 | java复制代码package com.jxau.queue; |
线程池
线程池:三大方法、7大参数、四种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!优化资源的使用!=》池化技术
线程池、连接池、内存池、对象池 ///….. 创建、销毁。十分浪费资源
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。
线程池的好处:
1、降低资源的消耗
2、提高响应的速度
3、方便管理
==线程复用、可以控制最大并发数、管理线程==
线程池:三大方法
1 | java复制代码package com.jxau.ThreadPool; |
源码分析:
1 | java复制代码public static ExecutorService newSingleThreadExecutor() { |
手动创建线程池
1 | java复制代码package com.jxau.ThreadPool; |
四种拒绝策略
1 | java复制代码/* |
小结和扩展
池的最大的大小如何去设置
了解:IO密集型,CPU密集型:(调优)
1 | java复制代码//最大线程该如何定义 |
函数式接口
lambda表达式、链式编程、函数式接口、stream流式计算
function函数式接口
1 | java复制代码package com.jxau.function; |
Predicate 断定型接口
1 | java复制代码package com.jxau.function; |
consumer 消费型接口
1 | java复制代码package com.jxau.function; |
supply 供给型接口
1 | java复制代码package com.jxau.function; |
流式计算
1 | java复制代码package com.jxau.stream; |
ForkJoin
什么是ForkJoin
ForkJoin在JDK1.7,并行执行任务!提高效率。大数据量!
大数据:Map Reduce(把大任务拆分为小任务)
ForkJoin 特点:工作窃取
异步回调
没有返回值的异步回调
1 | java复制代码package com.jxau.async; |
有返回值的异步回调
1 | java复制代码package com.jxau.async; |
JMM
谈谈你对Volatile的理解
volatile是java虚拟机提供轻量级的同步机制
1、保证可见性
2、不保证原子性
3、禁止指令重排
什么是JMM
JMM:java内存模型,不存在的东西,概念!约定!
关于JMM的一些同步的约定:
1、线程解锁前,必须把共享变量立刻刷回主存。
2、线程加锁前,必须读取主存中的最新值到工作内存中!
3、加锁和解锁是同一把锁
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)
- lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
- unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
- read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
- use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
- assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
- store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
- write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中
JMM对这八种指令的使用,制定了如下规则:
- 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
- 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
- 不允许一个线程将没有assign的数据从工作内存同步回主内存
- 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assig22n和load操作
- 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
- 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
- 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
- 对一个变量进行unlock操作之前,必须把此变量同步回主内存
Volatile
保证可见性
1 | java复制代码package com.jxau.tvolatile; |
2、不保证原子性
原子性:不可分割
线程A在执行任务的时候,不能被打扰的,也不能被分割。要么同时成功,要么同时失败。
1 | java复制代码package com.jxau.tvolatile; |
如果不加lock和synchronized,怎么样保证原子性
使用原子类解决原子性问题
这些类的底层和操作系统挂钩!
1 | java复制代码package com.jxau.tvolatile; |
指令重排
什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行。
源代码–>编译器优化的重排–>指令并行也可能会重排–>内存系统也会重排–>执行。
处理器在进行指令重排的时候,考虑:数据之间的依赖性!
1 | java复制代码int x=1;// 1 |
可能造成影响的结果:a b x v这四个值默认都是 0;
线程A | 线程B |
---|---|
x=a | y=b |
b=1 | a=2 |
正常的结果:x=0,y=0;
线程A | 线程B | |
---|---|---|
b=1 | a=2 | |
x=a | y=b |
指令重排导致的诡异结果:x=2;y=1;
非计算机专业
volatile可以避免指令重排
内存屏障。CPU指令。作用:
1、保证特定的操作的执行顺序!
2、可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)
volatile是可以保持可见性。不能保证原子性,由于内存屏障,可以保证指令重排的现象产生!
彻底玩转单例模式
饿汉式单例
构造器私有,别人无法去new出对象
1 | java复制代码package com.jxau.signal; |
懒汉式单例
1 | java复制代码package com.jxau.signal; |
反射破坏单例
1 | java复制代码package com.jxau.signal; |
三重检测锁———反射new出两个对象
1 | jaVA复制代码package com.jxau.signal; |
设置红绿灯—–反射发i百年关键字
1 | java复制代码package com.jxau.signal; |
内部类版
1 | java复制代码package com.jxau.signal; |
枚举类 反射无法破解
1 | java复制代码package com.jxau.signal; |
CAS
什么是CAS
CAS:比较当前工作内存中的值和主存中的值,如果这个值是期望的,那么则者自行操作!如果不是就不更新
缺点:
1、你会耗时
2、一次性只能保证一个你共享变量的原子性
3、ABA问题
1 | java复制代码package com.jxau.cas; |
Unsafe
对应的地址上如果是这个var5,那就加上var4(1)
内存操作,效率很高!
原子引用解决ABA问题
什么是ABA问题
在atomiinteger这个类中,他用cas 保证原子性问题,但同时也引发了新的问题;
ABA,一句话,狸猫换太子,举个例子,
(V,内存值,A旧的预期值,B,要求个更新值);
举例:
有两个线程,同时操作一个变量,线程1执行时间比线程2执行时间长,线程2执行快
线程1读取值,此时读到的值是A,这时候线程被挂起,
线程2也读到值,并将A修改为X,然后又做了操作,X又改为Z,最后又将Z改为A;线程2交出执行权;
线程1此时拿到执行权了,此时进行compareAndSwap,发现内存值和期望值是一样,于是正常执行,
但是内存值在这期间已经被操作过;
ABA问题带来的影响
aba不解决资源会被提前挪用,这不是我们所希望的
解决ABA问题,引入原子引用!对应的思想:乐观锁!
带版本号的原子操作!
1 | java复制代码package com.jxau.abaproblem; |
各种锁的理解
公平锁和非公平锁
公平锁:非常公平:需要排队等待其他线程执行完毕
非公平锁:非常不公平,可以插队(默认都是公平的)
1 | java复制代码 public ReentrantLock() { |
可重入锁
1 | java复制代码package com.jxau.locks; |
1 | java复制代码package com.jxau.locks; |
自旋锁
1 | java复制代码package com.jxau.locks; |
1 | java复制代码package com.jxau.locks; |
死锁
1 | java复制代码package com.jxau.locks; |
面试,工作中!排查问题:
1、日志
2、堆栈
本文转载自: 掘金