Java进阶13讲__第12讲_2/2

news2024/12/24 8:54:59

Ø线程安全问题

Ø线程同步方案

Ø线程池

Ø线程通信

Ø理论补充

1.  线程安全问题

1.1  举例说明

1.2  代码实现

package com.itheima.a_线程安全;

/*
线程安全:
    多个线程同时修改同一个资源

取钱案例
    小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元
    如果小明和小红同时来取钱,并且2人各自都在取钱10万元,可能会出现什么问题呢?

实现步骤
    1. 创建1个账户类,在类中定义账户金额和取钱方法
    2. 创建1个取钱线程类,在类中调用取钱方法
    3. 在主线程中创建1个账户对象代表二人的共享账户
    4. 在主线程中创建2个取钱线程,分别代表小明和小红,并将共享账户对象传递给2个线程处理
    5. 启动2个线程
*/
public class Demo {
    //3. 在主线程中创建1个账户对象代表二人的共享账户
    public static void main(String[] args) {
        Account account = new Account();
        //2. 创建1个取钱线程类,在类中调用取钱方法
        //4. 在主线程中创建2个取钱线程,分别代表小明和小红,并将共享账户对象传递给2个线程处理
        Person xHong = new Person(account);
        Person xMing = new Person(account);
        xMing.setName("小明");
        xHong.setName("小红");
        xMing.start();
        xHong.start();
    }
}

//5. 启动2个线程
class Person extends Thread {
    private Account account;

    public Person(Account account) {
        this.account = account;
    }

    @Override
    public void run() {
        account.drawMoney(100000);
    }
}

//1. 创建1个账户类,在类中定义账户金额和取钱方法
class Account {
    private Integer Money = 100000;

    public void drawMoney(Integer drawMoney) {
        if (drawMoney <= Money) {
            System.out.println(Thread.currentThread().getName() + "取钱" + drawMoney + "元成功");
            Money -= drawMoney;
            System.out.println(Thread.currentThread().getName() + "余额已更新");
            System.out.println(Thread.currentThread().getName() + "余额为:" + Money);
        } else {
            throw new RuntimeException("账户余额不足");
        }
    }
}

2.  线程同步方案

2.1  认识线程同步

2.2  方式一:同步代码块

package com.itheima.b_线程同步_同步代码块;
/*
同步代码块
    把访问共享资源的核心代码给上锁,以此保证线程安全

格式
    synchronized(同步锁){
        访问共享资源的核心代码
    }
原理
    每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
注意
    1、对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug注意
    2、同步锁不建议随便使用一个唯一的对象,也能锁住,但可能影响无关线程, 建议使用共享资源作为锁对象
        对于实例方法建议使用this作为锁对象规范
        对于静态方法建议使用字码(类名.class) 对象作为锁对象
*/
public class Account {
    //账户余额
    private Integer balance = 100000;
    //取钱
    public void drawMoney(Integer money) {
        //0.获取线程名称
        //1. 判断余额是否充足
        synchronized (this) {
            if (money > balance) {
                System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足");
                return;//方法结束
            }
            //2. 如果够,出钱
            System.out.println(Thread.currentThread().getName() + "取钱成功");
            //3. 更新余额
            balance -= money;
            System.out.println(Thread.currentThread().getName() + "取钱之后余额为:" + balance);
        }
    }
}

2.3  方式二:同步方法

package com.itheima.c_线程同步_同步方法;
/*
同步方法
    把访问共享资源的核心方法给上锁,以此保证线程安全。

格式
    修饰符 synchronized 返回值类型 方法名称(形参列表) {
    	操作共享资源的代码
    }
原理
    每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
    同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
    如果方法是实例方法:同步方法默认用this作为的锁对象。
    如果方法是静态方法:同步方法默认用类名.class作为的锁对象。
*/
public class Account {
    //账户余额
    private Integer balance = 100000;
    //取钱
    public synchronized void drawMoney(Integer money) {
        //0.获取线程名称
        //1. 判断余额是否充足
        if (money > balance) {
            System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足");
            return;//方法结束
        }
        //2. 如果够,出钱
        System.out.println(Thread.currentThread().getName() + "取钱成功");
        //3. 更新余额
        balance -= money;
        System.out.println(Thread.currentThread().getName() + "取钱之后余额为:" + balance);
    }
}

2.4  方式三:Lock锁

package com.itheima.d_线程同步_lock锁;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
Lock锁概述
    Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大
    Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建锁对象
方法
    public ReentrantLock() 创建锁对象
    public void lock()    上锁
    public void unlock() 释放锁
Lock锁使用规范
    规范1、锁对象创建在成员位置,使用final修饰
    规范2、释放锁的代码写在finally块中
*/
public class Account {
    //账户余额
    private Integer balance = 100000;
    private final Lock lock = new ReentrantLock();

    //取钱
    public void drawMoney(Integer money) {
        //0.获取线程名称
        String threadName = Thread.currentThread().getName();
        lock.lock();
        try {
            //1. 判断余额是否充足
            if (money > balance) {
                System.out.println(threadName + "取钱失败,余额不足");
                return;//方法结束
            }
            //2. 如果够,出钱
            System.out.println(Thread.currentThread().getName() + "取钱成功");
            //3. 更新余额
            balance -= money;
            System.out.println(Thread.currentThread().getName() + "取钱之后余额为:" + balance);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

2.5  三种线程同步方法对比

3.  线程池

3.1  认识线程池

3.1.1  定义 

3.1.2  线程池工作原理

3.1.3  线程池的执行流程

3.2  如何创建线程池?

3.3  线程池处理Runnable任务

package com.itheima.e_线程池;

import java.util.concurrent.*;

/*
线程池
    一个可以复用线程的技术
执行流程(核心线程-->工作队列-->临时线程-->拒绝策略)
    判断核心线程数是否已满,如果没满,则创建一个新的核心线程来执行任务
    如果核心线程满了,则判断工作队列是否已满,如果没满,则将任务存储在这个工作队列
    如果工作队列满了,则判断最大线程数是否已满,如果没满,则创建临时线程执行任务
    如果最大线程数已满,则执行拒绝策略
对象创建
    接口 ExecutorService
    实现类 ThreadPoolExecutor
        int corePoolSize : 指定线程池的核心线程的数量
        int maximumPoolSize:指定线程池的最大线程数量
        long keepAliveTime :指定临时线程的存活时间
        TimeUnit unit:指定临时线程存活的时间单位
        BlockingQueue<Runnable> workQueue:指定线程池的任务队列
        ThreadFactory threadFactory:指定线程池的线程工厂
        RejectedExecutionHandler handler:指定线程池的任务拒绝策略
*/
public class Demo1 {
    public static void main(String[] args) {
        ExecutorService es = new ThreadPoolExecutor(
                3,
                5,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        System.out.println(es);
        //核心线程
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
            try {
                Thread.sleep(25000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
            try {
                Thread.sleep(25000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
            try {
                Thread.sleep(25000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        //等待队列
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
        });
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
        });
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
        });
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
        });
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
        });
        //临时线程
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
            try {
                Thread.sleep(25000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
            try {
                Thread.sleep(25000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        es.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            System.out.println("执行任务");
        });
        System.out.println(es);
        //提交多次任务
//        for (int i = 0; i < 10; i++) {
//            es.execute(new Runnable() {
//                @Override
//                public void run() {
//                    System.out.println(Thread.currentThread());
//                    System.out.println("执行任务");
//                }
//            });
//        }

    }
}

3.4  线程池处理Callable任务

package com.itheima.e_线程池;

import java.util.concurrent.*;

/*
线程池执行Runnable任务
    Future<T> submit(Callable<T> task)	执行Callable任务,返回未来任务对象,用于获取线程返回的结果
    void shutdown()  等全部任务执行完毕后,再关闭线程池!
    List<Runnable> shutdownNow() 立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务
*/
public class Demo3 {
    public static void main(String[] args) throws Exception {
        ExecutorService es = new ThreadPoolExecutor(
                3,
                5,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        System.out.println(es);
        Future<Integer> future = es.submit(() -> {
            int count = 0;
            for (int i = 0; i < 11; i++) {
                if (i % 2 == 0) {
                    //遇到偶数休眠两秒
                    Thread.sleep(2000);
                }
                count += i;
            }
            return count;
        });
//        System.out.println("count:" + future.get());
        System.out.println("count:" + future.get(5, TimeUnit.SECONDS));
    }
}

案例

package com.itheima.e_线程池;

import java.util.concurrent.*;

/*
线程池执行Runnable任务
    Future<T> submit(Callable<T> task)	执行Callable任务,返回未来任务对象,用于获取线程返回的结果
    void shutdown()  等全部任务执行完毕后,再关闭线程池!
    List<Runnable> shutdownNow() 立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务
*/
public class Demo3 {
    public static void main(String[] args) throws Exception {
        ExecutorService es = new ThreadPoolExecutor(
                3,
                5,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        System.out.println(es);
        Future<Integer> future = es.submit(new MyTask(10));
        System.out.println(future.get());
        Future<Integer> future1 = es.submit(new MyTask(5));
        System.out.println(future1.get());
        Future<Integer> future2 = es.submit(new MyTask(100));
        System.out.println(future2.get());
        es.shutdown();
        System.out.println(es);
    }
}

//需求: 编写一个任务类, 可以通过构造器接收n, 计算并返回1~n的和
class MyTask implements Callable<Integer> {
    private int n;

    public MyTask(int n) {
        this.n = n;
    }

    /**
     * 计算1-n的和
     *
     * @return
     * @throws Exception
     */
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return sum;
    }
}

3.5  Executors工具类实现线程池

面试可能会问,了解

4.  线程通信(了解)

5.  理论补充

5.1  进程与线程

5.2  并发与并行

5.2.1  并发

5.2.2  并行

5.3  线程生命周期

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

西门子PLC与HMI之间的时间同步工控小周

HMI 时间同步功能工控人加入PLC工业自动化精英社群 HMI 设备具有时间同步功能&#xff0c;利用 HMI 设备的该功能&#xff0c;可实现 PLC 和 HMI 之间的时间同步&#xff0c;进而实现多个 PLC 之间的时间同步。 HMI 设备时间同步的属性&#xff1a; 1.HMI 设备既可作为主站对…

WinCC 中对 VBS 进行单步执行调试

以前应该写过文章给各位展示如何在WinCC 中通过自身控件对脚本&#xff08;C、VBS&#xff09;进行脚本诊断和排错。但是也有用户反馈说在编写了一些相对复杂的脚本后&#xff0c;WinCC自身控件无法做到单步调试&#xff0c;也会影响脚本的诊断调试效率。如果能够对WinCC 中的脚…

系统编程-多路IO复用

多路IO复用 目录 多路IO复用 三种方法实现多路IO复用 一、 select 函数 - 使用 select 来进行多路 io 复用的步骤 1、创建文件描述符集合 2、对集合进行清空操作 3、使用函数将要进行监测的文件描述符添加到集合中 4、对集合中的文件描述符进行监测 5、对哪个文件描述…

【网络安全】shortscan+ffuf+代码审计实现LFI和SSRF

未经许可,不得转载。 文章目录 正文正文 目标网页example.com: 使用IIS-ShortName-Scanner工具进行扫描: https://github.com/irsdl/IIS-ShortName-Scanner扫描结果如下: 这意味着目标容易受到IIS短文件名泄露的攻击。 IIS短文件名泄露: https://github.com/irsdl/IIS-S…

OpenCV-轮廓检测

文章目录 一、简介1. 意义2.具体步骤 二、代码实现三、总结 一、简介 1. 意义 在OpenCV中&#xff0c;轮廓检测是图像处理中一个非常重要的环节&#xff0c;它允许我们识别图像中的形状。这个过程通常涉及几个步骤&#xff1a;读取图像、转换为灰度图、应用阈值处理&#xff…

stm32驱动开发与linux驱动的区别

stm32&#xff0c;gpio设置原理 下图&#xff0c;定义了gpio E的基地址&#xff0c;只要将这个地址强制转换成gpiotypedf的类型&#xff0c;解析时&#xff0c;结构体地址就会自增。这样就可以对不同gpio组&#xff0c;就像定义。 全部gpio定义&#xff0c;强制为结构体类型…

C语言深度剖析--不定期更新的第六弹

extern关键字–最会帽子的关键字 看下面的代码&#xff1a; test.h #pragma once #include <stdio.h> extern int x;//声明不要进行初始化&#xff0c;也就是赋值&#xff0c;因为声明不开辟空间test.c #define _CRT_SECURE_NO_WARNINGS 1 #include "test.h&quo…

ESP-NETIF L2 TAP接口——启明云端乐鑫代理商

ESP-NETIF L2 TAP 概述 ESP-NETIF L2 TAP 接口是 ESP-IDF 访问用户应用程序中的数据链路层&#xff08;OSI/ISO 中的 L2&#xff09;以进行帧接收和传输的机制。在嵌入式开发中&#xff0c;它通常用于实现非 IP 相关协议&#xff0c;如 PTP 和 Wake on LAN 等。 Tips : 目前…

影响画布微信小程序canvas及skyline和webview用户界面布局的关键流程

影响微信小程序画布canvas及skyline和webview用户界面布局的关键流程 目录 影响微信小程序画布canvas及skyline和webview用户界面布局的关键流程 一、微信小程序canvas开发流程 1.1、官方指南 1.2、客制化开发 第一步&#xff1a;在 WXML 中添加 canvas 组件 第二步&…

【加密社】一行代码的漏洞,就蒸发了280亿美金!

加密社 2018年2月24日新闻,美图公司推出的第一个和区块链技术有关的项目叫做美链(英文缩写是BEC,全名叫做Beauty Chain) 这个项目发行的数字货币开始在一家叫OKEX的全球数字资产交易平台上可以买卖了。这个BEC币一共发了70亿个,价格曾经涨到了每个超过4美元,使得BEC的总价…

[A-09]ARMv8/ARMv9-Memory-内存空间(Address Spaces and Translation Regimes)

ver 0.2 更多精彩内容&#xff0c;请关注公众号 前言 任何人和组织的发展都需要空间&#xff0c;比如我们这个伟大的国家&#xff0c;幅员辽阔、大好河山决定了我们的发展潜力。这么大国土空间&#xff0c;不是随意无须的在发展&#xff0c;都是处于主动的规划(有形的手)或者…

跨境网红营销SOP流程1.0丨出海笔记

品牌出海利用红人营销基本是标配了&#xff0c;KOL 社交媒体是绝对的带货神器。比如美国歌手蕾哈娜Rihanna 的美妆品牌 Fenty Beauty 上市开卖后40天就达到了1亿美元&#xff0c;火遍全球美妆圈。例子和废话少说&#xff0c;其实大小红人都有用。 之前几位大神已经在出海笔记分…

网络编程入门-实现服务器与客户端通讯

概念学习 TCP概念&#xff1a; TCP&#xff08;Transmission Control Protocol&#xff09;协议指的是传输控制协议&#xff0c;是一个面向连接的传输协议&#xff0c;他是一个能提供高可靠性的通信协议&#xff0c;所谓高可靠性指的是数据无丢失、数据无误、数据无失序、数据…

【Linux】万字解读<进程控制>:创建&中止&等待&替换

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《…

2024年【甘肃省安全员C证】报名考试及甘肃省安全员C证考试总结

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年甘肃省安全员C证报名考试为正在备考甘肃省安全员C证操作证的学员准备的理论考试专题&#xff0c;每个月更新的甘肃省安全员C证考试总结祝您顺利通过甘肃省安全员C证考试。 1、【多选题】下列选用起重机钢丝绳时…

2 模拟——258. 各位相加 ★

2 模拟 258. 各位相加 ★ 给定一个非负整数 num&#xff0c;反复将各个位上的数字相加&#xff0c;直到结果为一位数。返回这个结果。 示例 1: 输入: num 38 输出: 2 解释: 各位相加的过程为&#xff1a; 38 --> 3 8 --> 11 11 --> 1 1 --> 2 由于 2 是一位…

RK3562 NPU供电要求

在《RK3562 NPU跑飞问题处理》文档中,有提及PWM_NPU网络对应的IO域电平,之前使用1.8V(PMUIO1电平),对应可以测量到VDD_NPU_P的电压为0.8V(上电后不工作状态),而最近硬件配置PMUIO1改为了3.3V,VDD_NPU_P的电压为0.6V,虽然部分AI模型能跑起来,但也存在NPU识别出错的情…

基于人工智能的实时交通监控系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 实时交通监控系统通过人工智能技术对交通流量进行监测和分析&#xff0c;能够帮助城市管理者优化交通流、减少拥堵、提升道路安全。本…

xilinx sdk退出Debug模式返回C开发布局模式

点击甲壳虫进入Debug窗口模式&#xff0c;退出方法是在最右上角quick access旁点击窗口布局的格子(open perspective)点击第一个default即可回到原先布局。

【python篇】——python基础语法一篇就能明白,快速理解

前言&#xff1a;本文章是在已经学习了C语言之后作者角度下进行书写的一篇关于python当中基础的语法说明。 文章目录 快速深入理解python当中的语法基本数据类型算数运算符类型的查看与转换 变量的输入与输出条件语句比较运算符和逻辑运算符if、elif、else三目运算符 循环语句w…