JUC进阶详细笔记

本文详细介绍了Java并发编程中的JUC(Java Util Concurrency)工具包,包括多线程环境搭建、JUC核心概念、线程与进程的区别、Lock锁的使用、生产者消费者问题的解决、并发集合类的安全问题、Callable的使用、常用辅助类如CountDownLatch、CyclicBarrier、Semaphore以及读写锁。还探讨了阻塞队列、线程池、函数式接口、Stream流式计算、ForkJoin框架、异步回调、JMM内存模型、Volatile特性、单例模式实现以及CAS和原子引用的原理和应用。
摘要由CSDN通过智能技术生成

多线程进阶-JUC并发

文章目录

1、搭建基础环境

1.1.新建一个Maven工程com.can
1.2.POM文件引入依赖

<dependencies>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

1.3.检查Project Settings的Project language level版本是:8- Lambdas, type annotations etc.
还有Modules-language level版本也要是:8- Lambdas, type annotations etc.
这样才能确保1.8的新特性可以使用
1.4.检查编译版本Settings-Java Compiler-Module版本为:1.8
准备环境就OK了


2、什么是JUC

源码+官方文档

面试高频问!

java.util.concurrent

java.util.concurrent.atomic 原子性

java.util.concurrent.locks lock

java.function

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iw1NZVlj-1587409579763)(C:\Users\Can\AppData\Roaming\Typora\typora-user-images\image-20200411154007524.png)]

java.util工具包

业务:普通的线程代码 Thread

**Runnale:**没有返回值,效率相比入 Callable 相对较低!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zrt3QOcL-1587409579766)(C:\Users\Can\AppData\Roaming\Typora\typora-user-images\image-20200414144746092.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a4VfGcN1-1587409579768)(C:\Users\Can\AppData\Roaming\Typora\typora-user-images\image-20200414145137417.png)]


3、线程和进程

线程、进程,如果不能一句话说出技术,就是不扎实

进程:

  • 一个程序,例如QQ.exe,Music.exe 程序的集合
  • 一个进程往往可以包含多个线程,至少包含一个。
  • Java默认有几个线程?2个,一个是main,一个是GC

线程:

  • 开了一个进程Typora,写字,自动保存(线程负责的)
  • Thread、Runnable、Callable

Java真的可以开启线程吗?开不了的

Thread源码

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 */
            }
        }
    }
	//本地方法,底层的C++,java无法操作硬件
    private native void start0();

native关键字,它只能通过本地方法去调用开启线程。

并发、并行

并发编程:并发、并行

并发(多线程操作同一资源)

  • CPU一核,模拟出多条线程,天下武功,唯快不破,快速交替形成一种假象

并行(多个人一起行走)

  • CPU多核,多个线程可以同时进行。线程池
package com.can;

public class TestAvailableProcessors获取CPU核数 {
   

    public static void main(String[] args) {
   
        //获取CPU的核数,
        //CPU 密集型,IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

并发编程的本质:充分利用CPU的资源

所有公司都很看重

企业,挣钱=>提高效率,裁员,找一个厉害的人顶替三个不怎么样的人。

人员(减)、技术成本(高)

线程有几个状态

/**
         * 新生
         */
        NEW,

        /**
         *运行
         */
        RUNNABLE,

        /**
         *阻塞
         */
        BLOCKED,

        /**
         * 等待,死死得等
         */
        WAITING,

        /**
         * 超时等待
         */
        TIMED_WAITING,

        /**
         * 终止
         */
        TERMINATED;

6个状态

wait/sleep区别

  1. 来自不同得类:

    • wait=>Object
    • sleep=>Thread
  2. 关于锁得释放

    • wait 会释放锁
    • sleep 抱着锁睡觉,不会释放锁
  3. 使用的范围不同

    • wait 必须在同步代码块中
    • sleep 可以在任何地方睡
  4. 是否需要捕获异常

    • wait 需要捕获异常

    • sleep 也需要捕获异常


4、Lock锁(重点)

传统的Synchronized

synchronized(深Q赖死)

package com.can;

//基本的卖票例子

import java.util.concurrent.locks.ReentrantLock;

/**
 * 真正的多线程开发,公司中的开发,降低耦合性
 * 线程就是一个单独的资源类,没有任何附属的操作
 * 1.属性、方法
 */
public class SaleTicket {
   
    public static void main(String[] args) {
   
        //并发,多线程操作同一资源类,把资源类丢入线程
        Ticket ticket = new Ticket();
        for (int i = 0; i < 60; i++) {
   
            new Thread(()->ticket.sale(),"A").start();
        }

        for (int i = 0; i < 60; i++) {
   
            new Thread(()->ticket.sale(),"B").start();

        }

        for (int i = 0; i < 60; i++) {
   
            new Thread(()->ticket.sale(),"C").start();

        }

    }
}

//资源类 OOP
class Ticket{
   
    //属性、方法
    private int number=50;

    //卖票的方式
    // synchronized 本质:队列,锁
    public synchronized void sale(){
   
            if(number>0){
   
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number);
            }
    }

}

Lock接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZXEA3zXl-1587409579771)(C:\Users\Can\AppData\Roaming\Typora\typora-user-images\image-20200414182059512.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PtRmgmu9-1587409579773)(C:\Users\Can\AppData\Roaming\Typora\typora-user-images\image-20200414182556771.png)]

公平锁:
  • 十分公平,可以先来后到
非公平锁:
  • 可以插队(默认是非公平锁)
package com.can;

//基本的卖票例子

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 真正的多线程开发,公司中的开发,降低耦合性
 * 线程就是一个单独的资源类,没有任何附属的操作
 * 1.属性、方法
 */
public class SaleTicketLock {
   
    public static void main(String[] args) {
   
        //并发,多线程操作同一资源类,把资源类丢入线程
        Ticket2 ticket = new Ticket2();
        for (int i = 0; i < 60; i++) new Thread(ticket::sale,"A").start();

        for (int i = 0; i < 60; i++) new Thread(()->ticket.sale(),"B").start();

        for (int i = 0; i < 60; i++) new Thread(()->ticket.sale(),"C").start();

    }
}

//资源类 OOP
//lock三部曲
//1.new ReentrantLock();
//2.lock.lock();加锁
//3.finally=>lock.unlock();解锁
class Ticket2{
   
    //属性、方法
    private int number=50;

    Lock lock = new ReentrantLock();
    //卖票的方式
    public void sale(){
   
        lock.lock();
        try {
   
            if(number>0){
   
                System.out.println(Thread.currentThread().getName()+"卖出了第"+(number--)+"张票,剩余"+number);
            }
        }finally {
   
            lock.unlock();
        }

    }

}

Synchronized和Lock锁区别:
  1. Synchronized 内置的Java关键字;Lock是一个Java类。
  2. Synchronized 无法判断获取锁的状态;Lock可以判断是否获取到了锁。
  3. Synchronized 会自动释放锁;Lock必须要手动释放!如果不释放锁,死锁。
  4. Synchronized 线程1(获得锁)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去。
  5. Synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置,默认非公平)。
  6. Synchronized 适合锁少量的代码同步问题;Lock适合锁大量的同步代码;

5、生产者和消费者问题

Synchronized版

package com.PC;

//线程之间的通信问题:生产者和消费者问题!等待唤醒,通知唤醒
//线程交替执行 A线程,B线程操作同一个变量,  num=0
//A  num+1
//B  num-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();
    }
}

//等待,业务,通知
//数字,资源类
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,C,D或者两个以上的线程就会存在问题!虚假唤醒

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K4Mgti7n-1587409579775)(C:\Users\Can\AppData\Roaming\Typora\typora-user-images\image-20200416175427829.png)]

if改为while判断,解决虚假唤醒问题

防止虚假唤醒的代码

package com.PC;

//线程之间的通信问题:生产者和消费者问题!等待唤醒,通知唤醒
//线程交替执行 A线程,B线程操作同一个变量,  num=0
//A  num+1
//B  num-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();
        new Thread(()->{
   
            for (int i = 0; i < 10; i++) {
   
                try {
   
                    data.decrement();
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            }
        },"C").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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-toFvoSBl-1587409579777)(C:\Users\Can\AppData\Roaming\Typora\typora-user-images\image-20200416190301539.png)]

image-20200416190401116

代码实现

package com.PC;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//condition.await();//等待
//condition.signalAll();//唤醒全部
public class B_Lock{
   
    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 < 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 Data2{
   
    private int number = 0;

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
//condition.await();//等待
//condition.signalAll();//唤醒全部
    //+1
    public void increment() throws InterruptedException {
   
        lock.lock();
        try {
   
            //业务代码
            while(number!=0){
   
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"==>"+number);
            //通知其他线程,我+1完毕了
            condition.signalAll();
        }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);
            //通知其他线程,我-1完毕了
            condition.signalAll();

        }finally {
   
            lock.unlock();
        }

    }
}


日志打印

image-20200416192602592

任何一个新的技术,绝对不是仅仅只是覆盖了原来的技术,优势和补充;

可以通过Condition精准的通知和唤醒线程,实现有序的唤醒

Condition实现精准的通知唤醒

condition(康第悉no)

image-20200416193414791
  • Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用

  • 一个Condition实例本质上绑定到一个锁。要获得特定Condition实例的Condition实例,请使用其newCondition()方法。

    package com.can.PC;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    //A执行完,调用B,B执行完,调用C,C执行完,调用A
    public class C_Condition精准通知唤醒 {
         
        public static void main(String[] args) {
         
            Data3 data3 = new Data3();
    
            new Thread(()->{
         
                for (int i = 0; i < 10; i++) {
         
                    data3.printA();
                }
            },"A").start();
            new Thread(()->{
         
                for (int i = 0; i < 10; i++) {
         
                    data3.printB();
                }
            },"B").start();
            new Thread(()->{
         
                for (int i = 0; i < 10; i++) {
         
                    data3.printC();
                }
            },"C").start();
        }
    }
    
    
    //资源类
    //Lock锁
    class Data3{
         
        private Lock lock = new ReentrantLock
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值