IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 多线程(三):线程池的安全问题 -> 正文阅读

[Java知识库]多线程(三):线程池的安全问题

学习内容:

1、 线程的上下文切换
2、 线程的安全(同步)问题
3、 解决线程安全问题方法及代码演示


线程的上下文切换

前提:一个CPU的内核一个时间只能运行一个线程中的一个指令

线程并发:CPU内核会在多个线程间来回切换运行,切换速度非常快,达到同时运行的效果

切换的话就会有三个问题

线程切换回来后,如何从上次执行的指令后执行?

程序计数器(每个线程都有,用于记录上次执行的行数)

线程执行会随时切换,如何保证重要的指令能完全完成?

线程安全问题

CPU进行上下文切换的过程中,性能会降低。
在这里插入图片描述

1、ALU:是能实现多组算术运算和逻辑运算的组合逻辑电路,简称ALU。

2、CU:是Control Unit控制单元,是CPU的一部分,用于执行计算机指令或者Client Unit 监控系统的监控客户端单元的一个计算机学名词

线程的安全(同步)问题

解释:当cpu在线程里来回切换,可能会导致丢失一些操作使整个执行流程缺失步骤,从而会影响到最终的数据问题

那么出现线程安全问题就需要三个条件

  • 多个线程
  • 同一个时间
  • 执行同一段指令或修改同一个变量

下面是一段银行转账案例

/**
 * 银行转账的案例
 */
public class BankDemo {

    //模拟100个银行账户
    private int[] accounts = new int[100];

    {
        //初始化账户
        for (int i = 0; i < accounts.length; i++) {
            accounts[i] = 10000;
        }
    }

    /**
     * 模拟转账
     */
    public void transfer(int from,int to,int money){
        if(accounts[from] < money){
            throw new RuntimeException("余额不足");
        }
        accounts[from] -= money;
        System.out.printf("从%d转出%d%n",from,money);
        accounts[to] += money;
        System.out.printf("向%d转入%d%n",to,money);
        System.out.println("银行总账是:" + getTotal());
    }

    /**
     * 计算总余额
     * @return
     */
    public int getTotal(){
        int sum = 0;
        for (int i = 0; i < accounts.length; i++) {
            sum += accounts[i];
        }
        return sum;
    }

    public static void main(String[] args) {
        BankDemo bank = new BankDemo();
        Random random = new Random();
        //模拟多次转账过程
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                int from = random.nextInt(100);
                int to = random.nextInt(100);
                int money = random.nextInt(2000);
                bank.transfer(from,to,money);
            }).start();
        }
    }
}

在这里插入图片描述

这里就出现了线程安全的问题,很明显的看出原本银行的总数在中间是发生了变化的。

线程安全问题的解决方法

所以就有了解决这个问题的方法,就是给这段程序上锁,让这段整体代码不受影响执行就上锁,执行完才释放,让其他线程在执行。

总共有三种上锁的方法:

  • 同步方法
  • 同步代码块
  • 同步锁

同步方法

给方法添加synchronized关键字作用是给整个方法上锁当前线程调用方法后,方法上锁,其它线程无法执行,调用结束后,释放锁

    /**
      * @Authoe: smz
      * @Description: 同步方法
      * @Date: 10:58 2021-12-08
      */
    public synchronized void trade(int from,int to,int num){
        if (arr[from]<num){
            throw new RuntimeException("余额不足了哦!");
        }
        //转出的减钱
        arr[from]-=num;
        System.out.println(from+"向"+to+"转出了"+num);
        //转入的加钱
        arr[to]+=num;
        System.out.println(to+"接收了"+from+"的"+num);

    }

同步代码块

任何对象都可以作为锁,对象不能是局部变量

    /**
      * @Authoe: smz
      * @Description: 同步块
      * @Date: 11:15 2021-12-08
      */
    synchronized (this){
        //转出的减钱
        arr[from]-=num;
        System.out.println(from+"向"+to+"转出了"+num);
        //转入的加钱
        arr[to]+=num;
        System.out.println(to+"接收了"+from+"的"+num);
        System.out.println(getTotal());
    }

同步锁

在java.concurrent并发包中的

Lock接口

基本方法:

  • lock() 上锁
  • unlock() 释放锁

常见实现类

  • ReentrantLock 重入锁
  • WriteLock 写锁
  • ReadLock 读锁
  • ReadWriteLock 读写锁

使用方法:

  1. 定义同步锁对象(成员变量)
  2. 上锁
  3. 释放锁
    private Lock lock = new ReentrantLock();

    public void trade(int from,int to,int num){
        lock.lock();
        if (arr[from]<num){
            throw new RuntimeException("余额不足了哦!");
        }
        try {
            //转出的减钱
            arr[from]-=num;
            System.out.println(from+"向"+to+"转出了"+num);
            //转入的加钱
            arr[to]+=num;
            System.out.println(to+"接收了"+from+"的"+num);
            System.out.println(getTotal());
        }finally {
            lock.unlock();
        }

    }

三种锁对比:

  1. 粒度(锁的范围)
    同步代码块/同步锁 < 同步方法
  2. 编程简便
    同步方法 > 同步代码块 > 同步锁
  3. 性能
    同步锁 > 同步代码块 > 同步方法
  4. 功能性/灵活性
    同步锁(有更多方法,可以加条件) > 同步代码块 (可以加条件) > 同步方法

锁对象:

  1. 非静态方法 --> this
  2. 静态方法 —> 当前类.class

synchronized的基本的原理

一旦代码被synchronized包含,JVM会启动监视器(monitor)对这段指令进行监控

线程执行该段代码时,monitor会判断锁对象是否有其它线程持有,如果其它线程持有,当前线程就无法执行,等待锁释放

如果锁没有其它线程持有,当前线程就持有锁,执行代码

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-12-10 10:55:44  更:2021-12-10 10:56:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:15:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码