1502 - JUC高并发

慢慢挣,今天比昨天更有钱,明天比今天有钱,后天比明天有钱。

0.思维导图

6.多线程锁

synchronized实现同步的基础:Java中的每一个对象都可以作为锁

具体表现为以下3中形式

  • 对于普通同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前类的Class对象。
  • 对于同步方法块,锁是Synchronized括号里配置的对象。

6.1 synchronized 锁的八种情况总结

视频案例

class Phone {
    // 模拟发送短信操作
    public synchronized void sendSMS() throws InterruptedException {
        // 停留3秒在短信内
        TimeUnit.SECONDS.sleep(3);
        System.out.println("------sendSMS------");
    }

    // 模拟发送邮件操作
    public synchronized void sendEmail() {
        System.out.println("------sendEmail------");
    }

    // 普通方法 打招呼
    public void sayHello() {
        System.out.println("------sayHello------");
    }

}


public class ThreadDemo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone1 = new Phone();
        // 创建一个线程,调用Phone的sendSMS()方法
        new Thread(() -> {
            try {
                phone.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        // 创建一个线程,调用Phone的sendEmail()方法
        new Thread(() -> {
            try {
//                phone.sayHello();
//                phone.sendEmail();
                phone1.sendEmail();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        ).start();
    }
}

不同案例输出的结果分析

  • 1.标准访问,先打印短信还是邮件

输出结果

------sendSMS------
------sendEmail------

原因分析

按线程启动顺序获取锁释放锁。

  • 2.停3秒在短信方法内,先打印短信还是邮件

输出结果

------sendSMS------
------sendEmail------

原因分析

此时获取同一个对象锁,第一次获取锁后等待3秒,执行完释放锁之后,第二个线程才能获取同一把对象锁。

  • 3.新增普通的hello方法,在线程2中调用,是先发短信还是hello

输出结果

------sayHello------
------sendSMS------
------sendEmail------

原因分析

线程A获取到锁,开始等待,因为线程B执行不许要获取同步锁,所以先输出sayHello,然后经过3秒,线程A等待结束,执行输出操作并释放锁。

  • 4.现在有两部手机,先打印短信还是邮件

输出结果

------sendEmail------
------sendSMS------

原因分析

线程A和B获取到不同的对象锁,之间没有竞争关系,因此B线程先输出,A先等待结束之后输出。

  • 5.两个静态同步方法,一部手机,先打印短信还是邮件

输出结果

------sendSMS------
------sendEmail------

原因分析

静态方法,是Class锁,线程A和B争夺同一把类锁,线程A 先获取锁,因此A等待3秒执行输出释放锁之后,B才获取到锁并执行输出。

  • 6.两个静态同步方法,两部手机,先打印短信还是邮件

输出结果

------sendSMS------
------sendEmail------

原因分析

线程A和B通过不同对象争夺同一把类锁,线程A 先获取锁,因此A等待3秒执行输出释放锁之后,B才获取到锁并执行输出。

  • 7.一个静态同步方法,一个普通同步方法,一部手机,先打印短信还是邮件

输出结果

------sendEmail------
------sendSMS------

原因分析

线程A获取类锁,执行等待,期间线程B获取对象锁,执行输出并释放对象锁,线程A等待结束执行输出释放类锁。(因为获取的锁对象不同,不存在竞争,按照时间顺序输出)

  • 8.一个静态同步方法,一个普通同步方法,两部手机,先打印短信还是邮件

输出结果

------sendEmail------
------sendSMS------

原因分析

通过不同对象获取不同的锁,不存在竞争。

6.2 公平锁和非公平锁

  • 公平锁 :效率相对低 ,阳光普照,每一个线程都参与工作。
  • 非公平锁:效率高,但是线程容易饿死,所有的工作,都由一个线程完成。

用法:在创建可重入锁ReentrantLock时,调用有参构造器,传入参数true设置为公平锁。

private final ReentrantLock lock = new ReentrantLock(true);

jdk源码

两者性能差距 

公平锁询问,如果有人,则进行排队等待

6.3 可重入锁

可重入锁就是某个线程已经获得某个锁,可以重复获取同一个锁而不死锁,可重入锁也叫递归锁。

synchronized示例代码

public class ThreadDemo {
    // 创建一个锁对象
    static final Object myLock = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (myLock) {
                System.out.println("这是第一层锁");
                synchronized (myLock) {
                    System.out.println("这是第二层锁");
                }
            }
        }).start();
    }
}

输出结果

ReentrantLock示例代码

public class ThreadDemo {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("这是第一层锁");
                lock.lock();
                try {
                    System.out.println("这是第二层锁");
                } finally {
                    lock.unlock();
                }
            } finally {
                lock.unlock();
            }
        }).start();
    }
}

6.4 死锁

两个或以上的进程因为争夺资源而造成互相等待资源的现象称为死锁。如果没有外力干涉,他们无法继续执行。

产生死锁的原因

  • 系统资源不足
  • 进行运行推进顺序不合理
  • 资源分配不当

死锁产生的四个必要条件

  • 互斥使用:当资源被一个线程使用或者占用时,别的线程不能使用该资源。
  • 不可抢占:获取资源的一方,不能从正在使用资源的一方抢占掠夺资源,资源只能被使用者主动释放。
  • 请求保持:资源请求者在请求别的资源时,同时保持对已有资源的占有。
  • 循环等待:即p1占有p2的资源,p2占有p3的资源,p3占有p1的资源,这样形成了一个等待环路。

死锁示例代码

public class ThreadDemo {
    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (a) {
                System.out.println("外层,已经获取a,试图获取b");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (b) {
                    System.out.println("内层");
                }
            }
        }, "A").start();

        new Thread(() -> {
            synchronized (b) {
                System.out.println("外层,已经获取b,试图获取a");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (a) {
                    System.out.println("内层");
                }
            }
        }, "B").start();
    }
}

7.Callable接口

7.1 Runnable接口与Callable接口

使用 Runnable 创建的线程缺少的一项功能,当线程终止时(即 run()完成时),我们无法使线程返回结果。为了支持此功能,Java 中提供了 Callable 接口,即线程终止(call()执行完成时)后返回结果。

对比Runnable接口Callable 接口
返回值没有
抛出异常没有
实现方法名称run()call()

7.2 Callable接口使用

代码示例

因为Thread的构造函数中没有Callable接口的参数设置,不可以直接替换,只能用FutureTask类来实现线程创建(FutureTask类既能传入Callable构造,又是Runnable接口的实现类)

class MyThread implements Callable {
    @Override
    public Integer call() throws Exception {
        return 10086;
    }
}

public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<>(new MyThread());

        //创建一个线程
        new Thread(futureTask,"AA").start();

        //调用FutureTask的get方法获取线程运行结果
        System.out.println(futureTask.get());
        System.out.println(Thread.currentThread().getName() + " is over");
    }
}

8.JUC强大辅助类

8.1 减少计数CountDownLatch

CountDownLatch 方法使用说明

CountDownLatch(int count);      //构造方法,创建一个值为count 的计数器。
await();      //阻塞当前线程,将当前线程加入阻塞队列。
countDown();      //对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。

案例
6个同学陆续离开教室之后,班长才能锁门。
如果不加 CountDownLatch类,会出现线程混乱执行,同学还未离开教室班长就已经锁门了。

不使用CountDownLatch,导致线程混乱

public class ThreadDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 6; i++) {
            new Thread(() -> System.out.println(Thread.currentThread().getName() + "同学离开教室"), String.valueOf(i)).start();
        }
        System.out.println(Thread.currentThread().getName() + "班长锁门");
    }
}

运行结果

 通过CountDownLatch计数,保证主线程输出语句最后执行

public class CountDownLatchDemo {
     public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "同学离开教室");
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "班长锁门");
    }
}

运行结果

8.2 循环栅栏CyclicBarrier

CyclicBarrier 方法使用说明

CyclicBarrier(int parties,Runnable barrierAction);      //构造方法,创建一个值为parties的屏障。
await();      //当一个线程到了栅栏这里了,那么就将计数器减 1

代码示例

public class CyclicBarrierDemo {
    public static final int NUMBER = 7;

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> System.out.println("集齐七颗龙珠就可以召唤神龙"));
        for (int i = 0; i < 7; i++) {
            new Thread(() -> {
                try {
                    System.out.println("第 " + Thread.currentThread().getName() + " 颗龙珠被收集到");
                    //等待
                    cyclicBarrier.await();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }, String.valueOf(i)).start();
        }
    }
}

运行结果

8.3 信号灯Semaphore

Semaphore 方法使用说明

Semaphore(int permits);      //创建具有给定的许可数和非公平的公平设置的Semapore
acquire();      //从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断
release();      //释放一个许可,将其返回给信号量

【具体案例】
6辆汽车,停3个车位

代码示例

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "车抢到车位");
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                    System.out.println("--" + Thread.currentThread().getName() + "车离开车位");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/701390.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go语音进阶 多任务

多任务 什么叫 多任务&#xff1f;简单说&#xff1a;就像是操作系统可以同时执行 多个任务。打个比方 你一边使用 浏览器上网&#xff0c;一遍在听MP3, 一边再用 word 赶作业。对于电脑来讲这就是多任务&#xff0c;还有很多任务悄悄的在后台同时运行着&#xff0c;只是桌面上…

【Ardiuno】实验使用ESP32单片机连接Wifi(图文)

ESP32单片机最为精华和有特色的地方当然是wifi连接&#xff0c;这里我们就写程序实验一下适使用ESP32主板连接wifi&#xff0c;为了简化实验我们这里只做了连接部分&#xff0c;其他实验在后续再继续。 由于本实验只要在串口监视器中查看结果状态即可&#xff0c;因此电路板上…

macOS 15 beta (24A5264n) Boot ISO 原版可引导镜像下载

macOS 15 beta (24A5264n) Boot ISO 原版可引导镜像下载 iPhone 镜像、Safari 浏览器重大更新、备受瞩目的游戏和 Apple Intelligence 等众多全新功能令 Mac 使用体验再升级 请访问原文链接&#xff1a;https://sysin.org/blog/macOS-Sequoia-boot-iso/&#xff0c;查看最新版…

如何有效释放Docker占用的存储空间

随着Docker的广泛应用&#xff0c;我们经常会遇到Docker占用过多存储空间的问题。这可能是由于频繁的镜像拉取、容器创建和删除等操作导致的。本文将介绍几种方法来有效释放Docker占用的存储空间&#xff0c;特别是docker system prune命令的使用。 Docker的存储机制 Docker使…

springboot连接多个库

一个SpringBoot项目&#xff0c;同时连接两个数据库&#xff1a;比如一个是Mysql数据库&#xff0c;一个是oracle数据库&#xff08;啥数据库都一样&#xff0c;连接两个同为oracle的数据库&#xff0c;或两个不同的数据库&#xff0c;只需要更改对应的driver-class-name和jdbc…

【C++】list 容器的增删改查---模拟实现(图例超详细解析!!!)

目录 一、前言 二、 list 容器的模拟实现思 ✨ 模块分析 ✨ 作用分析 三、list的节点类设计 四、list 的迭代器类设计 ⭐ 迭代器类--存在的意义 ⭐ 迭代器类--模拟实现 &#x1f4a6; 模板参数 和 成员变量 &#x1f4a6; 构造函数 &#x1f4a6; 运算符的重载 &…

HyperBDR新版本上线,自动化容灾兼容再升级!

本次HyperBDR v5.5.0版本新增完成HCS&#xff08;Huawei Cloud Stack&#xff09;8.3.x和HCSO&#xff08;Huawei Cloud Stack Online&#xff09;自动化对接&#xff0c;另外还突破性完成了Oracle云(块存储模式)的自动化对接。 HyperBDR&#xff0c;云原生业务级别容灾工具。支…

PS教程系统17

橡皮擦工具 主要配合画笔工具来使用 选择画笔工具新建图层试验擦除线条 如果直接在背景图片上进行擦除 会有背景颜色补充 背景橡皮擦 将其白色背景擦除掉shift相关键&#xff0c;进行工作区域切换吸取样点一次采样、两次采样连续、不连续等功能 在进行涂擦的过程一…

Unity EasyRoads3D插件使用

一、插件介绍 描述 Unity 中的道路基础设施和参数化建模 在 Unity 中使用内置的可自定义动态交叉预制件和基于您自己导入的模型的自定义交叉预制件&#xff0c;直接创建独特的道路网络。 添加额外辅助对象&#xff0c;让你的场景栩栩如生&#xff1a;桥梁、安全护栏、栅栏、墙壁…

RawChatGPT:公益大模型使用网站

文章目录 一、Rawchat介绍二、使用教程三、案例应用3.1 图片内容分析3.2 生图演示3.3 文档解析 一、Rawchat介绍 RawChat为用户提供了更为便捷的使用方式。 二、使用教程 RawChat公益站点链接&#xff1a;https://ChatGPTplus.cn 进入后&#xff0c;我们只需要点击&#xf…

基于Java+Swing+mysql幼儿园信息管理系统V2

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

深入理解rtmp(三)之手把手实现握手协议

RTMP是基于TCP协议的应用层协议,默认通信端口1935.实现握手协议前先了解一下rtmp握手协议吧!!! 握手过程 要建立一个有效的RTMP Connection链接&#xff0c;首先要“握手”:客户端要向服务器发送C0,C1,C2&#xff08;按序&#xff09;三个chunk&#xff0c;服务器向客户端发送…

Linux1(介绍与基本命令)

目录 一、初始Linux 1. Linux的起源 2. Linux是什么&#xff1f; 3. Linux内核版本 4. Linux的应用 5. 终端 6. Shell 7. Linux目录结构 二、基本命令 1. 基本的命令格式 2. shutdown 关机命令 3. pwd 当前工作目录 4. ls 查看目录内容 5. cd 改变工作目录 …

揭秘速卖通API接口:打破电商边界,用代码驱动全球业务增长

速卖通&#xff08;AliExpress&#xff09;通常指的是阿里巴巴集团旗下的国际零售电商平台。然而&#xff0c;直接通过API接口与速卖通进行交互通常涉及阿里巴巴的开放平台&#xff08;Open Platform&#xff09;和相关API。由于API的具体细节、认证方式、请求参数和返回值可能…

六种图算法的python实现

六种图算法的python实现 1. Prim 算法 基本原理 Prim算法是一种求解最小生成树的贪心算法。所谓最小生成树&#xff0c;就是对于给定的连通图&#xff0c;找到一棵包含所有顶点的树&#xff0c;且树上所有边的权重之和最小。Prim算法从一个顶点开始&#xff0c;每次选择与当…

数据丢失?揭秘easyrecovery破解版下载安装步骤教程,一键恢复!

“我不小心把硬盘里的重要文件删了&#xff0c;怎么都找不到了&#xff01;” “电脑突然崩溃了&#xff0c;所有的数据都没了&#xff0c;怎么办&#xff1f;” 这些情况是不是让你感到绝望&#xff1f;不过别担心&#xff0c;EasyRecovery数据恢复软件可以帮你轻松解决这些问…

[office] excel表格中双击鼠标左键有什么快捷作用- #经验分享#媒体

excel表格中双击鼠标左键有什么快捷作用? excel表格中双击鼠标左键有什么快捷作用&#xff1f;不要小看鼠标左键双击的作用&#xff0c;在excel中双击鼠标左键可以实现六个功能&#xff0c;提高工作效率&#xff0c;到底是那六个功能呢&#xff1f;请看下文详细介绍 在表格中…

R语言绘图 --- 桑基图(Biorplot 开发日志 --- 5)

「写在前面」 在科研数据分析中我们会重复地绘制一些图形&#xff0c;如果代码管理不当经常就会忘记之前绘图的代码。于是我计划开发一个 R 包&#xff08;Biorplot&#xff09;&#xff0c;用来管理自己 R 语言绘图的代码。本系列文章用于记录 Biorplot 包开发日志。 相关链接…

React基础教程:TodoList案例

todoList案例——增加 定义状态 // 定义状态state {list: ["kevin", "book", "paul"]}利用ul遍历list数组 <ul>{this.state.list.map(item ><li style{{fontWeight: "bold", fontSize: "20px"}} key{item.i…

MoE大模型大火,AI厂商们在新架构上看到了什么样的未来?

文 | 智能相对论 作者 | 陈泊丞 很久以前&#xff0c;在一个遥远的国度里&#xff0c;国王决定建造一座宏伟的宫殿&#xff0c;以展示国家的繁荣和权力。他邀请了全国最著名的建筑师来设计这座宫殿&#xff0c;这个人以其卓越的才能和智慧闻名。 然而&#xff0c;这位建筑师…