Java并发JUC(java.util.concurrent)锁

简介: Java并发JUC(java.util.concurrent)锁

在这里插入图片描述

👨🏻‍🎓博主介绍:大家好,我是芝士味的椒盐,一名在校大学生,热爱分享知识,很高兴在这里认识大家🌟
🌈擅长领域:Java、大数据、运维、电子
🙏🏻如果本文章各位小伙伴们有帮助的话,🍭关注+👍🏻点赞+🗣评论+📦收藏,相应的有空了我也会回访,互助!!!
🤝另本人水平有限,旨在创作简单易懂的文章,在文章描述时如有错,恳请各位大佬指正,在此感谢!!!

@[TOC]

什么是JUC?

  • java.util.concurrent在并发编程中使用的工具类

进程/线程是什么?

  • 进程:一个程序。例如:QQ。
  • 线程:调用进程的资源实现一些功能。例如:在QQ下,我们可以聊天也可以打电话。
  • ⚠️ Tips:Java默认有两个线程,一个是main、一个是GC,开线程的三种方法:Thread(普通的线程代码)、Runnable(没有返回值,效率比Callable低)、Callable,Java无法真正的开线程,无法操作硬件,底层使用的是C++,调用本地方法,

并发/并行是什么?

  • 并发:多个线程操作一个资源,假的多线程,就是快速交替。
  • 并行:多个线程同时执行。

线程几种状态

  • 线程有六种状态

    public enum State {
                    //新生
            NEW,
                    //运行
            RUNNABLE,
                    //阻塞
            BLOCKED,
            //等待,死等
            WAITING,
                    //超时等待
            TIMED_WAITING,
                    //终止
            TERMINATED;
        }
  • wait/sleep区别?

    1. wait→object,sleep→Thread。
    2. wait会释放锁,sleep不会释放锁。
    3. wait必须在同步代码块中,sleep任何地方都可以睡。
    4. wait不用捕获异常,sleep必须要捕获异常

传统锁synchronized

public class ReviewSync {
    public static void main(String[] args) {
        //资源类
        Takits takits = new Takits();
        //线程
        new Thread(()->{
            for (int i = 1; i < 66; i++) {
                takits.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 1; i < 66; i++) {
                takits.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 1; i < 66; i++) {
                takits.sale();
            }
        },"C").start();
    }
}
class Takits{
    private int tick =66;
    public synchronized void sale(){
        if (tick>0){
            System.out.println(Thread.currentThread().getName()+":"+(tick--)+"还剩:"+tick);
        }
    }
}

Lock锁(重点)

  • 先给大家上个锁分类

在这里插入图片描述

  • lock接口

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

在这里插入图片描述


公平锁:十分公平:可以先来后到

**非公平锁:十分不公平:可以插队 (默认)**

public class ReviewLock {
    public static void main(String[] args) {
        //资源类
        SalveTicks salveTicks = new SalveTicks();
        //线程
        new Thread(()->{ for (int i = 0; i < 108; i++) {salveTicks.sale();}},"A").start();
        new Thread(()->{ for (int i = 0; i < 108; i++) {salveTicks.sale();}},"B").start();
        new Thread(()->{ for (int i = 0; i < 108; i++) {salveTicks.sale();}},"C").start();

    }
}
class SalveTicks{
    private int tick_num=99;
    //锁放外卖,里面每次的锁都不一样,无法锁
    Lock lock =new ReentrantLock();
    public void sale(){
        lock.lock();
        try {
            if (tick_num>0){
                System.out.println(Thread.currentThread().getName()+":"+(tick_num--)+"还剩:"+tick_num);
            }
        }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 适合锁大量的同步代码!

生产者和消费者问题

  • Synchronized版本

    package icu.lookyousmileface.generatorandconsumer;
    
    /**
     * @author starrysky
     * @title: GeneraAndConsumerSync
     * @projectName Juc_Pro
     * @description: sync版本生产者和消费者
     * @date 2021/1/299:11 上午
     * 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
     * 线程交替执行 A B 操作同一个变量 num = 0
     * A num+1
     * B num-1
     */
    public class GeneraAndConsumerSync {
        public static void main(String[] args) {
            // 资源类
            Data data = new Data();
            //线程
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.inc();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"A").start();
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.dec();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"B").start();
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.dec();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"C").start();
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.dec();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"C").start();
        }
    }
    class Data{
        private int number = 0;
    
        public synchronized void inc() throws InterruptedException {
            while (number!=0){
                this.wait();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+":"+"number:"+number);
            //唤醒所有线程
            this.notifyAll();
        }
    
        public synchronized void dec() throws InterruptedException {
            while (number==0){
                //等待唤醒。睡眠
                this.wait();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+":"+"number:"+number);
            this.notifyAll();
        }
    }

    ⚠️ Tips:wait应该处于while循环等待中,不应该是if,否则会有虚假唤醒的问题。面试的:单例模式、排序算法、生产者和消费者、死锁

  • JUC版本(线程非顺序)

    • sync和juc版本的替换

      在这里插入图片描述

    package icu.lookyousmileface.generatorandconsumer;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author starrysky
     * @title: GeneraAndConsumerSync
     * @projectName Juc_Pro
     * @description: juc版本生产者和消费者
     * @date 2021/1/299:11 上午
     * 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
     * 线程交替执行 A B 操作同一个变量 num = 0
     * A num+1
     * B num-1
     */
    public class GeneraAndConsumerJUC1 {
        public static void main(String[] args) {
            // 资源类
            Datas data = new Datas();
            //线程
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.inc();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"A").start();
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.dec();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"B").start();
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.dec();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"C").start();
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.dec();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"C").start();
        }
    }
    
    class Datas{
        private int number = 0;
        Lock lock = new ReentrantLock();
            //代替了原本的监视器
        Condition condition = lock.newCondition();
    
        public  void inc() throws InterruptedException {
           //加锁
            lock.lock();
            try {
                while (number!=0){
                    condition.await();
                }
                number++;
                System.out.println(Thread.currentThread().getName()+":"+"number:"+number);
                            //通知其他线程
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
    
        }
    
        public  void dec() throws InterruptedException {
            lock.lock();
            try {
                while (number==0){
                    //等待唤醒。睡眠
                    condition.await();
                }
                number--;
                System.out.println(Thread.currentThread().getName()+":"+"number:"+number);
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
    
        }
    }

    ⚠️ Tips:这样的JUC是线程无序的

  • JUC版本(线程顺序)

    package icu.lookyousmileface.generatorandconsumer;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author starrysky
     * @title: GeneraAndConsumerSync
     * @projectName Juc_Pro
     * @description: juc版本生产者和消费者
     * @date 2021/1/299:11 上午
     * Condition+ flage标志位实现线程顺序执行
     */
    public class GeneraAndConsumerJUC2 {
        public static void main(String[] args) {
            // 资源类
            Data2 data = new Data2();
            //线程
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.A();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"A").start();
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.B();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"B").start();
            new Thread(()->{
                for (int i = 0; i < 50; i++) {
                    try {
                        data.C();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"C").start();
    
        }
    }
    
    class Data2{
    
        final Lock lock = new ReentrantLock();
        final Condition condition1 = lock.newCondition();
        final Condition condition2 = lock.newCondition();
        final Condition condition3 = lock.newCondition();
        private int flage = 1;
    
        public  void A() throws InterruptedException {
            lock.lock();
            try {
                //不是1就睡眠等待
                while (flage!=1){
                    condition1.await();
                }
                flage++;
                System.out.println(Thread.currentThread().getName()+":"+"A");
                //唤醒指定的线程
                condition2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
    
        }
    
        public  void B() throws InterruptedException {
            lock.lock();
            try {
                while (flage!=2){
                    condition2.await();
                }
                flage++;
                System.out.println(Thread.currentThread().getName()+":"+"B");
                condition3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
            public  void C() throws InterruptedException {
                lock.lock();
                try {
                    while (flage != 3) {
                        condition3.await();
                    }
                    flage=1;
                    System.out.println(Thread.currentThread().getName() + ":" + "C");
                    condition1.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
    }

    ⚠️ Tips:Condition优势精准通知和唤醒线程。

8 锁现象

  • 锁会锁住:实例对象,Class模版
  • 8锁实验代码:

    Gitee托管

    ⚠️ Tips:new this 是一个手机,static class是一个手机模版

各种锁的理解

1、公平锁、非公平锁

公平锁: 非常公平, 不能够插队,必须先来后到!

非公平锁:非常不公平,可以插队 (默认都是非公平)

//通过在构造时传入的boolean值选择公平或非公平,默认是非公平
public ReentrantLock() {
        sync = new NonfairSync();
    }

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

2、可重入锁

  • 可重入锁(递归锁)
  • 图解

    在这里插入图片描述

package icu.lookyousmileface.matchlock;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author starrysky
 * @title: RepeatLock
 * @projectName Juc_Pro
 * @description: 可重复锁,拿到大门的锁相当于拿到卧室的钥匙,lock版本,sync就是作用方法即可
 * @date 2021/1/308:56 下午
 */
public class RepeatLock {
    public static void main(String[] args) {

        Phone phone = new Phone();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                5,
                Runtime.getRuntime().availableProcessors(),
                5,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );

        try {
            threadPoolExecutor.execute(()->{
                phone.sms();
                System.out.println(Thread.currentThread().getName()+"=>ok");
            });

            threadPoolExecutor.execute(()->{
                phone.sms();
                System.out.println(Thread.currentThread().getName()+"=>ok");
            });
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            threadPoolExecutor.shutdown();
        }

    }

    static class Phone {
        ReentrantLock reentrantLock = new ReentrantLock();

        public void sms() {
            /**
             * 上锁和解锁要配对否则会造成死锁
             */
            reentrantLock.lock();
            try {
                System.out.println("Sms");
                call();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }

        public void call() {
            reentrantLock.lock();
            try {
                System.out.println("call");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }
    }
}

3、自旋锁

  • 自己创建自旋锁

    * @author starrysky
     * @title: MyLocks
     * @projectName Juc_Pro
     * @description: 使用CAS创建的自旋锁
     * @date 2021/1/309:16 下午
     */
    public class MyLocks {
        private final  AtomicReference<Thread> locks = new AtomicReference<>();
    
        public void upLock() {
            System.out.println(Thread.currentThread().getName()+"=>上锁");
            //自旋锁
            while (!locks.compareAndSet(null,Thread.currentThread())){
    
            }
        }
    
        public void downLock() {
            System.out.println(Thread.currentThread().getName()+"=>解锁");
            locks.compareAndSet(Thread.currentThread(),null);
        }
    }
    pool-1-thread-1=>上锁
    pool-1-thread-2=>上锁
    pool-1-thread-1=>解锁
    pool-1-thread-2=>解锁
    • 从最开始的上锁开始解锁,前面的锁没有解除,后面的将无法解锁。

4、死锁
在这里插入图片描述

  • 试验代码

    /**
     * @author starrysky
     * @title: DeadLockTest
     * @projectName Juc_Pro
     * @description: 死锁测试
     * @date 2021/1/309:46 下午
     */
    public class DeadLockTest {
        public static void main(String[] args) {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    5,
                    Runtime.getRuntime().availableProcessors(),
                    5, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.CallerRunsPolicy()
            );
    
            try {
                threadPoolExecutor.execute(new MyDeadThread("lockA","lockB"));
    
                threadPoolExecutor.execute(new MyDeadThread("lockB","lockA"));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
            threadPoolExecutor.shutdown();
            }
        }
    }
    /**
     * @author starrysky
     * @title: MyDeadThread
     * @projectName Juc_Pro
     * @description: 线程类
     * @date 2021/1/309:51 下午
     */
    public class MyDeadThread implements Runnable{
    
        private  String lockA;
        private  String lockB;
    
        public MyDeadThread(String lockA, String lockB) {
            this.lockA = lockA;
            this.lockB = lockB;
        }
    
        @Override
        public void run() {
            synchronized (lockA){
                System.out.println(Thread.currentThread().getName()+"=>lockA");
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB){
                    System.out.println(Thread.currentThread().getName()+"=>lockB");
                }
            }
        }
    }
  • 排除死锁

    • 使用jps -l查看正在运行的demo,并记录其PID
    • 使用jstack PID可以查看到该程序的堆栈的信息
  • ⚠️ Tips:

    排查问题:

    1、日志 9

    2、堆栈 1

相关文章
|
2月前
|
安全 Java API
JAVA并发编程JUC包之CAS原理
在JDK 1.5之后,Java API引入了`java.util.concurrent`包(简称JUC包),提供了多种并发工具类,如原子类`AtomicXX`、线程池`Executors`、信号量`Semaphore`、阻塞队列等。这些工具类简化了并发编程的复杂度。原子类`Atomic`尤其重要,它提供了线程安全的变量更新方法,支持整型、长整型、布尔型、数组及对象属性的原子修改。结合`volatile`关键字,可以实现多线程环境下共享变量的安全修改。
|
14天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
1月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
19天前
|
Java 数据库连接 数据库
如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面
本文介绍了如何构建高效稳定的Java数据库连接池,涵盖连接池配置、并发控制和异常处理等方面。通过合理配置初始连接数、最大连接数和空闲连接超时时间,确保系统性能和稳定性。文章还探讨了同步阻塞、异步回调和信号量等并发控制策略,并提供了异常处理的最佳实践。最后,给出了一个简单的连接池示例代码,并推荐使用成熟的连接池框架(如HikariCP、C3P0)以简化开发。
40 2
|
1月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
27 1
|
2月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
2月前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
11天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
20天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
7天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
28 9