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
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。
|
1天前
|
安全 Java 大数据
Java性能优化(七)-多线程调优-并发容器的使用
Java性能优化(七)-多线程调优-并发容器的使用
13 0
|
1天前
|
存储 并行计算 Java
Java8中JUC包同步工具类深度解析(Semaphore,CountDownLatch,CyclicBarrier,Phaser)
Java8中JUC包同步工具类深度解析(Semaphore,CountDownLatch,CyclicBarrier,Phaser)
9 2
|
6天前
|
缓存 安全 Java
【Java面试——并发基础、并发关键字】
随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略: 先进行操作,如果没有其它线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。这种乐观的并发策略的许多实现都不需要将线程阻塞,因此这种同步操作称为非阻塞同步。 乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。硬件支持的原子性操作最典型的是: 比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。
|
7天前
|
缓存 安全 Java
Java的线程池与并发工具类技术性文章
Java的线程池与并发工具类技术性文章
10 0
|
7天前
|
监控 Java 编译器
Java的内存模型与并发控制技术性文章
Java的内存模型与并发控制技术性文章
14 2
|
7天前
|
安全 Java 程序员
Java的迭代器与并发集合的技术性文章
Java的迭代器与并发集合的技术性文章
5 0
|
7天前
|
安全 Java 编译器
Java并发编程中的锁优化策略
【5月更文挑战第30天】 在多线程环境下,确保数据的一致性和程序的正确性是至关重要的。Java提供了多种锁机制来管理并发,但不当使用可能导致性能瓶颈或死锁。本文将深入探讨Java中锁的优化策略,包括锁粗化、锁消除、锁降级以及读写锁的使用,以提升并发程序的性能和响应能力。通过实例分析,我们将了解如何在不同场景下选择和应用这些策略,从而在保证线程安全的同时,最小化锁带来的开销。
|
8天前
|
安全 Java 开发者
Java并发编程中的锁优化策略
【5月更文挑战第30天】 在Java并发编程领域,锁机制是实现线程同步的关键手段之一。随着JDK版本的发展,Java虚拟机(JVM)为提高性能和降低延迟,引入了多种锁优化技术。本文将深入探讨Java锁的优化策略,包括偏向锁、轻量级锁以及自旋锁等,旨在帮助开发者更好地理解和应用这些高级特性以提升应用程序的性能。
|
8天前
|
Java 编译器
Java并发编程中的锁优化策略
【5月更文挑战第30天】在Java并发编程中,锁是一种重要的同步机制,用于保护共享资源。然而,不当的使用可能导致性能下降,甚至死锁。本文将探讨Java并发编程中的锁优化策略,包括锁粗化、锁消除、锁降级等方法,以提高程序的性能。