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

相关文章
|
7天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
3天前
|
Java 数据库
JAVA并发编程-一文看懂全部锁机制
曾几何时,面试官问:java都有哪些锁?小白,一脸无辜:用过的有synchronized,其他不清楚。面试官:回去等通知! 今天我们庖丁解牛说说,各种锁有什么区别、什么场景可以用,通俗直白的分析,让小白再也不怕面试官八股文拷打。
|
3天前
|
安全 Java 开发者
Java并发编程中的锁机制解析
本文深入探讨了Java中用于管理多线程同步的关键工具——锁机制。通过分析synchronized关键字和ReentrantLock类等核心概念,揭示了它们在构建线程安全应用中的重要性。同时,文章还讨论了锁机制的高级特性,如公平性、类锁和对象锁的区别,以及锁的优化技术如锁粗化和锁消除。此外,指出了在高并发环境下锁竞争可能导致的问题,并提出了减少锁持有时间和使用无锁编程等策略来优化性能的建议。最后,强调了理解和正确使用Java锁机制对于开发高效、可靠并发应用程序的重要性。
13 3
|
15天前
|
Oracle Java 关系型数据库
【颠覆性升级】JDK 22:超级构造器与区域锁,重塑Java编程的两大基石!
【9月更文挑战第6天】JDK 22的发布标志着Java编程语言在性能和灵活性方面迈出了重要的一步。超级构造器和区域锁这两大基石的引入,不仅简化了代码设计,提高了开发效率,还优化了垃圾收集器的性能,降低了应用延迟。这些改进不仅展示了Oracle在Java生态系统中的持续改进和创新精神,也为广大Java开发者提供了更多的可能性和便利。我们有理由相信,在未来的Java编程中,这些新特性将发挥越来越重要的作用,推动Java技术不断向前发展。
|
4月前
|
数据可视化 Java 测试技术
Java 编程问题:十一、并发-深入探索1
Java 编程问题:十一、并发-深入探索
67 0
|
4月前
|
存储 设计模式 安全
Java 编程问题:十、并发-线程池、可调用对象和同步器2
Java 编程问题:十、并发-线程池、可调用对象和同步器
53 0
|
4月前
|
缓存 安全 Java
Java 编程问题:十、并发-线程池、可调用对象和同步器1
Java 编程问题:十、并发-线程池、可调用对象和同步器
56 0
|
1月前
|
安全 Java 调度
解锁Java并发编程高阶技能:深入剖析无锁CAS机制、揭秘魔法类Unsafe、精通原子包Atomic,打造高效并发应用
【8月更文挑战第4天】在Java并发编程中,无锁编程以高性能和低延迟应对高并发挑战。核心在于无锁CAS(Compare-And-Swap)机制,它基于硬件支持,确保原子性更新;Unsafe类提供底层内存操作,实现CAS;原子包java.util.concurrent.atomic封装了CAS操作,简化并发编程。通过`AtomicInteger`示例,展现了线程安全的自增操作,突显了这些技术在构建高效并发程序中的关键作用。
58 1
|
2月前
|
安全 Java 开发者
Java并发编程:理解并发安全与性能优化
在当今软件开发中,Java作为一种广泛使用的编程语言,其并发编程能力显得尤为重要。本文深入探讨了Java中的并发编程,包括如何确保并发安全性以及优化并发程序的性能。通过分析常见的并发问题和解决方案,读者将能够更好地理解如何利用Java的并发工具包来构建可靠和高效的多线程应用程序。 【7月更文挑战第10天】
|
3月前
|
Java 调度
Java多线程编程与并发控制策略
Java多线程编程与并发控制策略