狂神说Java-JUC并发编程学习视频地址
解释 | 归属 | 备注 | |
---|---|---|---|
util | |||
Callable | 可赎回 | concurrent | |
concurrent | 并发 | ||
TimeUnit | 时间单位 | ||
DAYS | 天 | TimeUnit | |
SECONDS | 秒 | TimeUnit | |
Condition | 条件,精准通知 | lock | |
Reentrant | 可重入的 | lock | |
notify all | 通知所有人 | synchronized | |
await | 等待(加锁) | Condition | |
signal | 信号(解锁) | Condition | |
Vector | 矢量 | 集合锁 | |
Collections | 收藏 | 集合锁 | |
CopyOnWriteArrayList<> | 写入时复制 | 集合锁 | |
call | 呼叫 | Callable | |
Future tas | 适配类 | Runnable | |
CountDownLatch | 减计数器(倒计时器) | 辅助类 | |
CyclicBarrier | 加法计数器 | 辅助类 | |
Semaphore | 信号量 | 辅助类 | |
acquire() | 得到 | Semaphore | |
release( | 释放 | Semaphore | |
ReadWriteLock | 读写锁 | lock | |
Queue | 排队 | ||
lockingQueue | 阻塞 | 阻塞队列 | |
ArrayDeque | 双端队列 | 阻塞队列 | |
BlockingQueue | 阻塞队列 | FIFO | |
Array | 数组 | ||
remove | 去除 | ArrayBlockingQueue | 抛出异常,boolean |
element | 首元素 | ArrayBlockingQueue | 抛出异常,boolean |
offer | 给与 | ArrayBlockingQueue | 不抛,有返回值 |
poll | 得到 | 不抛,有返回值 | |
peek | 偷看 | 不抛,有返回值 | |
take | 拿 | 阻塞,等待 | |
SynchronousQueue | 同步队列 | 阻塞队列 | 写<->读 |
Executors | 执行器 | 线程池 | |
Single | 单 | Executors | 单线程 |
Fixed | 固定 | Executors | 固定大小 |
Cached | 缓存 | Executors | 可伸缩 |
shutdown | 关闭 | Executors | 线程池关闭 |
ThreadPoolExecutor | 线程池执行器 | ThreadPoolExecutor | |
corePoolSize | 核心线程池大小 | ThreadPoolExecutor | |
maximumPoolSize | 最大线程池大小 | ThreadPoolExecutor | |
keepAliveTime | 保持活动时间 | ThreadPoolExecutor | |
workQueue | 阻塞队列大小 | ThreadPoolExecutor | |
Rejectedr…Handler | 拒绝策略 | ThreadPoolExecutor | 拒绝后的动作 |
AbortPolicy | 终止策略 | Rejected…Handler | 不处理,抛出 |
CallerRuns | 呼叫方运行 | 哪来去哪去 | |
Discard | 丢弃 | 丢弃,不抛异常 | |
DiscardOldest | 丢弃最旧的 | 和最早的试竞争,不抛 | |
execute | 执行 | ThreadPoolExecutor | |
Runtime | 运行时 | ||
availableProcessors | 可用处理器 | 获取电脑的核数 | |
foreach | 函数包 | foreach | |
function | 函数接口 | foreach | 输入值1,返回值1 |
Predicate | 断定型接口 | foreach | 输入值1,返回boolean |
Consumer | 消费型接口 | foreach | 只有输入,没有返回 |
Supplier | 供给型接口 | foreach | 没有参数,只有返回值 |
filter | 过滤器 | stream | 继承Predicate |
map | 绘制 | stream | 继承Function |
toUpperCase | 改为大写 | stream | |
sorted | 分类 | stream | 继承Consumer |
limit | 限量 | stream | |
forEach | 对于每个 | stream | 继承Consumer |
ForkJoin | 双端队列 | ForkJoin | 工作窃取 |
fork | 交叉 | ForkJoin | 把任务压入线程列队 |
join | 参加 | ForkJoin | 结果合并 |
ForkJoinTask | ForkJoin父类 | ForkJoin | 任务要继承 |
ForkJoinPool | ForkJoin执行 | ForkJoin | |
.execute | 计算任务:执行 | ForkJoin执行 | 没有返回结果 |
.submit | 计算任务:提交 | ForkJoin执行 | 有返回结果 |
rangeClosed | 范围已关闭,半包括 | Stream | |
parallel | 并行 | Stream | |
reduce | 归约计算 | Stream | |
Future | 异步回调,未来 | Future | 未来建模 |
CompletableFuture | 完全未来 | Future | 比较常用的实现类 |
runAsync | 异步运行 | Future | 没有返回值 |
supplyAsync | 异步供给 | Future | 有返回值 |
whenComplete | 完成时 | Future | 正常,错误 |
Volatile | 不稳定 | JMM | 同步机制 |
Read | 读取 | 内存交互操作 | |
Load | 载入 | 内存交互操作 | |
Use | 使用 | 内存交互操作 | |
assign | 赋值 | 内存交互操作 | |
store | 存储 | 内存交互操作 | |
activeCount | 活动计数 | Thead | |
enum | 枚举 | ||
compareAndSet | 比较并交换 | CAS | |
expect | 期望值 | CAS | |
update | 更新值 | CAS | |
getStamp | 获得版本号 | CAS | |
ReentrantLock | 公平锁 | 各种锁 | |
spinlock | 自旋锁 | 各种锁 |
确认IEDA jdk版本号
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210705093845509.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3J6ejY1NDUyMDY0,size_16,color_FFFFFF,t_70
学习方向:源码+官方文档,面试高频问
图 官方文档java.util
java.util 工具包、包、分类
业务:普通的线程代码 Thread
Runnable 没有返回值、效率相比 Callable 相对较低!
图 Callable
图 Lock
线程、进程,如果不能使用一句话说出来的技术,不扎实!
进程:一个程序,QQ.exe Music.exe 程序的集合;
一个进程往往可以包含多个线程,至少包含一个!
Java默认有几个线程?2个 mian、GC
线程例如:开了一个进程Typora,写字,自动保存,这些功能都是线程负责
对于Java而言:Thread、Runable、Callable之前我们使用这三个开启线程。
提问?JAVA真的可以开启线程吗? 开不了的!
start调用的 native 是本地方法,属于底层的 C++,Java 无法直接操作硬件
代码:start源码
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
并发、并行
并发编程:并发、并行
并发: 多线程操作同一个资源
并行: 多个人一起行走
代码:获取CPU的核数
public class Test1 {
public static void main(String[] args) {
//获取CPU的核数
//CPU 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程的本质:充分利用CPU的资源
程序就是在现有的基础上更有效的利用CPU的资源
线程有几种状态
6种
代码:线程状态源码
* @since 1.5
* @see #getState
*/
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
//新生
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
//运行
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
//阻塞
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
*
* - {@link Object#wait() Object.wait} with no timeout
* - {@link #join() Thread.join} with no timeout
* - {@link LockSupport#park() LockSupport.park}
*
*
* A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called Object.wait()
* on an object is waiting for another thread to call
* Object.notify() or Object.notifyAll() on
* that object. A thread that has called Thread.join()
* is waiting for a specified thread to terminate.
*/
//等待,一直等
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
*
* - {@link #sleep Thread.sleep}
* - {@link Object#wait(long) Object.wait} with timeout
* - {@link #join(long) Thread.join} with timeout
* - {@link LockSupport#parkNanos LockSupport.parkNanos}
* - {@link LockSupport#parkUntil LockSupport.parkUntil}
*
*/
//超时等待,限时等待
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
//终止
TERMINATED;
}
wait / sleep 区别
来自不同的类
wait => Object
sleep => Thread
一般情况企业中使用休眠是:
TimeUnit.DAYS.sleep(1); //休眠1天
TimeUnit.SECONDS.sleep(1); //休眠1s
关于锁的释放
wait 会释放锁;
sleep睡觉了,不会释放锁;
使用的范围是不同的
wait 必须在同步代码块中;
sleep 可以在任何地方睡;
是否需要捕获异常
wait是不需要捕获异常;
sleep必须要捕获异常;
传统的Synchronized
多线程 学习阶段 vs 公司开发
学习阶段的多线程
代码:多线程开启
package JVMclass.lock01;
/*
* 学习阶段的多线程
* 1.创造一个多线程
* 2.重写run方法
* 3.new Thread开启
* 4....
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
new Thread(new MyThread()).start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
}
}
公司开发生成资源类,再用多线程开启其中的某一方法
代码:资源类多线程开启
package JVMclass.lock01;
//卖票系统
/*
* 真正的多线程开发,公司中的开发,降低耦合性
* 线程就是一个单独的资源类,没有任何附属的操作
* 1.属性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
//并发:多线程操作同一个资源类
Ticket ticket = new Ticket();
//@FunctionalInterface 函数式接口,jdk1.8 lambda表达式(参数)->{代码},”name“
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}},"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}},"C").start();
}
}
//资源类 OOP面向对象编程
//如果直接implements Runnable接口,他将只是单独的线程了,会有较高的耦合性
class Ticket {
//属性 方法
private int number =50;
//卖票的方式
//synchronized本质:排队、锁
public synchronized void sale(){
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余:"+number);
}
}
}
Lock 接口
图 Lock帮助文档
Condition (精确通知)
Lock 锁
ReadWriteLock 读写锁
Lock 锁
图 加锁,解锁使用格式
图 Lock 三种实现类
ReentrantLock 可重入锁(常用)
ReadLock 读锁
WriteLock 写锁
ReentrantLock 可重入锁(常用)
图 ReentrantLock源码,公平锁,非公平锁
公平锁: 十分公平,必须先来后到~;
非公平锁: 十分不公平,可以插队;(默认为非公平锁)
代码:ReentrantLock 可重入锁
package com.juc.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test1 {
public static void main(String[] args) {
//并发:多线程操作同一个资源类,把资源类丢入线程
Ticket2 ticket = new Ticket2();
//简化
new Thread(()->{
for (int i = 0; i < 60; i++) ticket.sale(); },"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) ticket.sale(); },"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) ticket.sale(); },"C").start();
}
//Lock三部曲
/*
* 1.new ReentrantLock();
* 2. .lock(); //加锁
* 3. .unlock(); //解锁 (解锁需要在代码块中)
*/
static class Ticket2 {
//属性,方法
private int number =50;
Lock lock = new ReentrantLock();
public synchronized void sale(){
lock.lock(); //加锁
try {
//业务代码
if(number>0){
System.out.println(
Thread.currentThread().getName()+"卖出了第"+(number--)+"票,剩余:"+number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); //解锁
}
}
}
}
Synchronized 和 Lock区别
1、Synchronized 内置的Java关键字,Lock是一个Java类
2、Synchronized 无法判断获取锁的状态,Lock可以判断
3、Synchronized 会自动释放锁,lock必须要手动加锁和手动释放锁!可能会遇到死锁
4、Synchronized 线程1(获得锁->阻塞)、线程2(等待->一直等待);
lock就不一定会一直等待下去,lock会有一个trylock去尝试获取锁,不会造成长久的等待。
5、Synchronized 是可重入锁,不可以中断的,非公平的;
Lock,可重入的,可以判断锁,可以自己设置公平锁和非公平锁;
6、Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码;
锁是什么?如何判断锁的是谁
面试的:单例模式、排序模式、生产者和消费者、死锁
代码:生产,消费,+1,-1
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"B").start();
}
}
/*
* 1.当程序第一次过来number = 0时,如果执行B~decrement(),由于if成立,B等待,不能执行。
* 经过increment(),发现if不成立,所以直接++,并通知其他线程可执行,B~decrement()解冻。
* 2.第二次当number = 1时,经过如果A~increment(),if成立,所以A等待。
* B执行,经过decrement(),if不成立,所以--,并解冻A~increment()
* 3.第三次number = 0,由于A~increment()已经被解冻,并且if不成立,所以++
*...
*/
class Data{
//数字 资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if(number!=0){
//等待操作
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if(number==0){
//等待操作
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 我-1完毕了
this.notifyAll();
}
}
问题存在,A线程B线程,现在如果我有四个线程A B C D!
当四个线程时,或更多线程时,使用 if 时会出现虚假唤醒,多次加、减
解决方案: if 改为while即可,防止虚假唤醒
图 虚假唤醒问题
代码:if 改为while,防止虚假唤醒
package com.juc.test.PC;
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"B").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"C").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"D").start();
}
}
class Data{
//数字 资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
while (number!=0){
//等待操作
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (number==0){
//等待操作
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 我-1完毕了
this.notifyAll();
}
}
JUC版本的生产者和消费者问题
通过Lock 找到 Condition
图 Synchronized等待通知,Lock的Condition等待通知
图 Condition 内的Lock 等待通知
代码 Lock Condition 等待 通知
package com.juc.test.PC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"B").start();
new Thread(()->{
for(int i=0;i<12;i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"C").start();
new Thread(()->{
for(int i=0;i<10;i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}},"D").start();
}
}
class Data2{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await(); 加锁
//condition.signal(); 解锁
public void increment() throws InterruptedException {
lock.lock();
try {
//业务代码
while (number!=0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public void decrement() throws InterruptedException {
lock.lock(); //加锁
try {
while (number==0){
condition.await(); //等待
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll(); //通知
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); //解锁
}
}
}//signalAll();全部唤醒,.signal指定唤醒
问题:
任何一个新的技术,绝对不是仅仅为了覆盖原来的技术,它会更有优势,或得到了补充
Condition的优势:精准的通知和唤醒的线程!
如果我们要指定通知的下一个进行顺序怎么办呢? 我们可以使用Condition来指定通知进程~
代码:用Condition来指定通知进程
/**
* A 执行完 调用B
* B 执行完 调用C
* C 执行完 调用A
*/
public class C {
public static void main(String