JUC并发编程---狂神说Java学习笔记

JUC并发编程

狂神说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版本号

JUC并发编程---狂神说Java学习笔记_第1张图片![在这里插入图片描述](https://img-blog.csdnimg.cn/20210705093845509.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3J6ejY1NDUyMDY0,size_16,color_FFFFFF,t_70
JUC并发编程---狂神说Java学习笔记_第2张图片

  • 修改IDEA中准备环境

1、什么是JUC

学习方向:源码+官方文档,面试高频问

JUC并发编程---狂神说Java学习笔记_第3张图片

图 官方文档java.util

java.util 工具包、包、分类

业务:普通的线程代码 Thread

Runnable 没有返回值、效率相比 Callable 相对较低!

JUC并发编程---狂神说Java学习笔记_第4张图片

图 Callable

JUC并发编程---狂神说Java学习笔记_第5张图片

图 Lock

  • 以前多线程所学的 CallableLock 都存在于java.util.concurrent包下

2、线程和进程

线程、进程,如果不能使用一句话说出来的技术,不扎实!

进程:一个程序,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 只有一核,模拟出来多条线程,天下武功,唯快不破。那么我们就可以使用CPU快速交替,来模拟多线程。

并行: 多个人一起行走

  • CPU多核,多个线程可以同时执行。 我们可以使用线程池

代码:获取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 区别

  1. 来自不同的类

    wait => Object

    sleep => Thread

    一般情况企业中使用休眠是:

    TimeUnit.DAYS.sleep(1); //休眠1天

    TimeUnit.SECONDS.sleep(1); //休眠1s

  2. 关于锁的释放

​ wait 会释放锁;

​ sleep睡觉了,不会释放锁;

  1. 使用的范围是不同的

    wait 必须在同步代码块中;

    sleep 可以在任何地方睡;

  2. 是否需要捕获异常

    wait是不需要捕获异常;

    sleep必须要捕获异常;

3、Lock锁(重点)

传统的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 接口

JUC并发编程---狂神说Java学习笔记_第6张图片

图 Lock帮助文档

  • Condition (精确通知)

  • Lock 锁

  • ReadWriteLock 读写锁

Lock 锁

在这里插入图片描述

图 加锁,解锁使用格式

JUC并发编程---狂神说Java学习笔记_第7张图片

图 Lock 三种实现类

  • ReentrantLock 可重入锁(常用)

  • ReadLock 读锁

  • WriteLock 写锁

ReentrantLock 可重入锁(常用)

JUC并发编程---狂神说Java学习笔记_第8张图片

图 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适合锁大量的同步代码;

锁是什么?如何判断锁的是谁

4、生成者和消费者问题

面试的:单例模式、排序模式、生产者和消费者、死锁

代码:生产,消费,+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即可,防止虚假唤醒

JUC并发编程---狂神说Java学习笔记_第9张图片

图 虚假唤醒问题

代码: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

JUC并发编程---狂神说Java学习笔记_第10张图片

图 Synchronized等待通知,Lock的Condition等待通知

JUC并发编程---狂神说Java学习笔记_第11张图片

图 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指定唤醒

问题:

  • Lock 和 Synchronized 并无两样
  • 多线程是随机的,多为先运行AB,再运行CD,但我们想让它A,B,C,D有序的运行
  • 所以…

任何一个新的技术,绝对不是仅仅为了覆盖原来的技术,它会更有优势,或得到了补充

Condition的优势:精准的通知和唤醒的线程!

如果我们要指定通知的下一个进行顺序怎么办呢? 我们可以使用Condition来指定通知进程~

代码:用Condition来指定通知进程

/**
 * A 执行完 调用B
 * B 执行完 调用C
 * C 执行完 调用A
   */
public class C {
   
public static void main(String

你可能感兴趣的:(java)