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
Java并发编程进阶:深入理解Java内存模型
Java并发编程进阶:深入理解Java内存模型
39 0
|
18天前
|
安全 Java API
JAVA并发编程JUC包之CAS原理
在JDK 1.5之后,Java API引入了`java.util.concurrent`包(简称JUC包),提供了多种并发工具类,如原子类`AtomicXX`、线程池`Executors`、信号量`Semaphore`、阻塞队列等。这些工具类简化了并发编程的复杂度。原子类`Atomic`尤其重要,它提供了线程安全的变量更新方法,支持整型、长整型、布尔型、数组及对象属性的原子修改。结合`volatile`关键字,可以实现多线程环境下共享变量的安全修改。
|
22天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
13天前
|
Java API 容器
JAVA并发编程系列(10)Condition条件队列-并发协作者
本文通过一线大厂面试真题,模拟消费者-生产者的场景,通过简洁的代码演示,帮助读者快速理解并复用。文章还详细解释了Condition与Object.wait()、notify()的区别,并探讨了Condition的核心原理及其实现机制。
|
1月前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
105 6
【Java学习】多线程&JUC万字超详解
|
2月前
|
存储 Java
Java 中 ConcurrentHashMap 的并发级别
【8月更文挑战第22天】
36 5
|
2月前
|
存储 算法 Java
Java 中的同步集合和并发集合
【8月更文挑战第22天】
25 5
|
2月前
|
缓存 Java 调度
【Java 并发秘籍】线程池大作战:揭秘 JDK 中的线程池家族!
【8月更文挑战第24天】Java的并发库提供多种线程池以应对不同的多线程编程需求。本文通过实例介绍了四种主要线程池:固定大小线程池、可缓存线程池、单一线程线程池及定时任务线程池。固定大小线程池通过预设线程数管理任务队列;可缓存线程池能根据需要动态调整线程数量;单一线程线程池确保任务顺序执行;定时任务线程池支持周期性或延时任务调度。了解并正确选用这些线程池有助于提高程序效率和资源利用率。
41 2
|
4月前
|
安全 Java 程序员
Java并发编程中的锁机制与优化策略
【6月更文挑战第17天】在Java并发编程的世界中,锁是维护数据一致性和线程安全的关键。本文将深入探讨Java中的锁机制,包括内置锁、显式锁以及读写锁的原理和使用场景。我们将通过实际案例分析锁的优化策略,如减少锁粒度、使用并发容器以及避免死锁的技巧,旨在帮助开发者提升多线程程序的性能和可靠性。
|
3月前
|
存储 缓存 Java
Java面试题:解释Java中的内存屏障的作用,解释Java中的线程局部变量(ThreadLocal)的作用和使用场景,解释Java中的锁优化,并讨论乐观锁和悲观锁的区别
Java面试题:解释Java中的内存屏障的作用,解释Java中的线程局部变量(ThreadLocal)的作用和使用场景,解释Java中的锁优化,并讨论乐观锁和悲观锁的区别
37 0
下一篇
无影云桌面