java多线程学习(二)

多线程学习(一):http://t.csdnimg.cn/o3ygn

目录

一、线程安全

二、线程同步

三、加锁的实现方式一:同步代码块

四、加锁的实现方式二:同步方法

五、同步方法和同步代码块的比较

六、加锁的实现方式三:Lock锁


一、线程安全

(1)什么是线程安全

多个线程,在操作同一共享资源时,可能会出现的业务问题

例如:取钱的线程安全问题

A和B两人同时去银行取钱一万元,对同一个账户进行操作,A使用银行卡,B使用存折,卡内余额一万元。

取钱流程:

1.判断余额是否足够

2.余额足够取钱

3.更新余额

多线程执行时,当A执行到第一步,判断余额足够取钱,还未执行到第三步。这时候B也进行取钱,这时候判断余额也是足够的,因此两人都可以进行取钱操作。在取钱之后更新时余额就成了负一万元。

(2)用代码模拟线程安全问题

(1)提供一个账户类,创建一个对象代表两人的共享账户

(2)定义一个线程类(创建两个线程,分别代表两个人A和B)

(3)创建两个线程,传入同一个账户对象给两个线程处理

package com.txd.demo4;

//账户类
public class Account {

    private double money;

    //A和B取钱时传过来的取钱金额
    public void money(double money){
        //搞清楚谁取钱(获取线程名)
        String name = Thread.currentThread().getName();
        //判断余额是否足够
        if(this.money >= money){
            System.out.println(name+"来取钱"+money+"成功");
            this.money =  this.money - money;
            System.out.println(name+"来取钱,余额剩余"+this.money);
        }else {
            System.out.println(name+"来取钱,余额不足");
        }
    }

    public Account() {
    }

    public Account(double money) {
        this.money = money;
    }

    /**
     * 获取
     * @return money
     */
    public double getMoney() {
        return money;
    }

    /**
     * 设置
     * @param money
     */
    public void setMoney(double money) {
        this.money = money;
    }

    public String toString() {
        return "Account{money = " + money + "}";
    }
}
package com.txd.demo4;


//代表取钱的线程类
public class MoneyThread extends Thread{

    //接受账户信息    和 取款人
    private Account account;
    public MoneyThread(Account account, String name){
        super(name);
        this.account = account;
    }

    @Override
    public void run() {
        //取钱操作(A和B)
        account.money(10000);
    }
}

        

执行结果

二、线程同步

线程同步就是解决线程安全问题的方案

线程同步的思想:

让多个线程实现先后依次访问共享资源,这样就解决了安全问题

线程同步的常见方案:

加锁:每次只允许一个线程加锁,加锁后才能访问,访问完毕后自动解锁,然后其他线程才能再加锁进来

三、加锁的实现方式一:同步代码块

作用:

把共享资源的核心代码给上锁,以此保证线程的安全

synchronized(同步锁){
    访问共享资源的核心代码
}

原理:

每次只允许一个线程加锁后进入,执行完毕自动解锁,其他线程才可以进来执行

同步锁的注意事项:

对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug

取钱案例改造:

选择核心代码,在该案例中核心代码就是取钱的代码,也就是

//判断余额是否足够
        if(this.money >= money){
            System.out.println(name+"来取钱"+money+"成功");
            this.money =  this.money - money;
            System.out.println(name+"来取钱,余额剩余"+this.money);
        }else {
            System.out.println(name+"来取钱,余额不足");
        }

我们可以对这串代码进行加锁,选中这串代码,使用快捷键ctrl+alt+t选择synchronized

在括号中也就是同步锁必须是同一个对象,例如字符串"测试"

加锁后执行代码查看控制台输出:

测试成功。因为A线程优先启动,所以会有限竞争到锁先执行方法        

执行步骤:

当A和B线程同时启动,都执行到这个取钱方法的时候,就开始竞争同步锁,当A线程拿到锁后,会对这个对象进行标记,即表示被加锁。B线程就无法进入方法。当A线程执行方法结束后,会自动解锁,此时B线程拿到锁执行方法。

因为使用了固定字符串为同步锁,当其他账户来取钱时也因为加锁导致无法取钱 。因此应该使用他们的账户信息作为锁,不同账户互不干扰,也就是类中的this。

如果是静态资源,可以使用类名.class作为锁        Account.class

注意事项:

如果锁对象随便选一个唯一的对象,会影响其他无关线程的执行

使用规范:

应该使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象

对于静态资源建议使用字节码(类名.class)对象作为锁对象

四、加锁的实现方式二:同步方法

作用:

把访问共享资源的核心方法上锁,保证线程安全

修饰符 synchronized 返回值类型 方法名称(形参列表){
    操作共享资源的代码
}

原理:

每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行

同步方法底层也是有隐式锁对象的,只是锁的范围是整个方法代码

取钱案例改造:

找到核心方法:

//A和B取钱时传过来的取钱金额
    public void money(double money){
        //搞清楚谁取钱(获取线程名)
        String name = Thread.currentThread().getName();
        //判断余额是否足够
            if(this.money >= money){
                System.out.println(name+"来取钱"+money+"成功");
                this.money =  this.money - money;
                System.out.println(name+"来取钱,余额剩余"+this.money);
            }else {
                System.out.println(name+"来取钱,余额不足");
            }
    }

只需要在方法上加上synchronized

执行main方法:

执行步骤:

当多个线程到这个实例方法的时候,底层也是使用this作为锁,只是看不到

如果是静态方法,默认的锁是类名.class作为锁

五、同步方法和同步代码块的比较

范围上:同步代码块锁的范围更小,同步方法锁的范围更大(锁的范围越小性能越好)

可读性:同步方法更好

六、加锁的实现方式三:Lock锁

Lock是接口,不能直接实例化,可以采用他的实现类ReentrantLock来构建Lock锁对象

Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活,更方便,更强大

使用:

创建一个lock锁对象

注意事项:

//创建一个锁对象
private Lock lock = new ReentrantLock();

时建议使用final进行修饰

对需要上锁的代码进行try catch  finally 防止加锁后出现异常没有解锁

 public void money(double money){
        //搞清楚谁取钱(获取线程名)
        String name = Thread.currentThread().getName();
        lock.lock();    //加锁
        //判断余额是否足够
        try {
            if(this.money >= money){
                System.out.println(name+"来取钱"+money+"成功");
                this.money =  this.money - money;
                System.out.println(name+"来取钱,余额剩余"+this.money);
            }else {
                System.out.println(name+"来取钱,余额不足");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();  //解锁
        }
    }

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

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

相关文章

zookeeper快速入门一:zookeeper安装与启动

本文是zookeeper系列之快速入门中的第一篇,欢迎大家观看与指出不足。 写在前面: 不影响教程,笔者安装zookeeper用的是WSL(windows下的linux子系统),当然你想直接在windows上用zookeeper也是可以的。 如果你也想用ws…

全国农产品价格分析预测可视化系统设计与实现

全国农产品价格分析预测可视化系统设计与实现 【摘要】在当今信息化社会,数据的可视化已成为决策和分析的重要工具。尤其是在农业领域,了解和预测农产品价格趋势对于农民、政府和相关企业都至关重要。为了满足这一需求,设计并实现了全国农产…

向量相似性度量的常用方法

向量相似性度量的常用方法 0. 引言1. 欧氏距离(Euclidean distance)2. 余弦相似度(Cosine similarity)3. 汉明距离(Hamming distance)4. 点积相似度 (Dot Product Similarity)5. 曼哈顿距离 (Manhattan Distance) 0. 引言 今天花时间学习学习向量相似性度量的常用方法&#xf…

[蓝桥杯练习题]确定字符串是否包含唯一字符/确定字符串是否是另一个的排列

确定字符串是否包含唯一字符 #include<bits/stdc.h> using namespace std; int main(){ios::sync_with_stdio(0);cin.tie(nullptr);cout.tie(nullptr);map<char,int>m;string s;cin>>s;for(int i0;i<s.size();i){if(isalpha(s[i]))s[i]tolower(s[i]);if(…

Mock.js了解(Mock就是模拟一个后端,Postman模拟前端)

JSON5 Node.js Vue CLI与Mock.js Jquery与Mock.js Mock与分页

NeRF学习——NeRF-Pytorch的源码解读

学习 github 上 NeRF 的 pytorch 实现项目&#xff08;https://github.com/yenchenlin/nerf-pytorch&#xff09;的一些笔记 1 参数 部分参数配置&#xff1a; 训练参数&#xff1a; 这段代码是在设置一些命令行参数&#xff0c;这些参数用于控制NeRF&#xff08;Neural Radi…

智慧城市与数字孪生:共创未来城市的智慧生活

目录 一、智慧城市与数字孪生的概念与特点 二、智慧城市与数字孪生共创智慧生活的路径 1、城市规划与建设的智能化 2、城市管理与服务的智慧化 3、城市安全与应急管理的智能化 三、智慧城市与数字孪生面临的挑战与对策 四、智慧城市与数字孪生的发展趋势与展望 1、技术…

redis中List和hash数据类型

list类型是用来存储多个有序的字符串的&#xff0c;列表当中的每一个字符看做一个元素&#xff0c;一个列表当中可以存储一个或者多个元素&#xff0c;redis的list支持存储2^32-1个元素。redis可以从列表的两端进行插入&#xff08;pubsh&#xff09;和弹出&#xff08;pop&…

游戏引擎中网络游戏的基础

一、前言 网络游戏所面临的挑战&#xff1a; 一致性&#xff1a;如何在所有的主机内都保持一样的表现可靠性&#xff1a;网络传输有可能出现丢包安全性&#xff1a;反作弊&#xff0c;反信息泄漏。多样性&#xff1a;不同设备之间链接&#xff0c;比如手机&#xff0c;ipad&a…

专升本 C语言笔记-07 逗号运算符

1.逗号表达式的用法 就是用逗号隔开的多个表达式。逗号表达式&#xff0c;从左向右依次执行。 2.逗号表达式的特性 2.1.当没有括号时&#xff0c;第一个表达式为整个表达式的值。 代码 int x 3,y 5,a 0; a x,y; printf("a %d",a); 说明:因为逗号优先级最低,会…

OpenCV4.9.0开源计算机视觉库在 Linux 中安装

返回目录&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV 环境变量参考 下一篇&#xff1a;将OpenCV与gcc和CMake结合使用 引言&#xff1a; OpenCV是一个开源的计算机视觉库&#xff0c;由英特尔公司所赞助。它是一个跨…

确保云原生部署中的网络安全

数字环境正在以惊人的速度发展&#xff0c;组织正在迅速采用云原生部署和现代化使用微服务和容器构建的应用程序&#xff08;通常运行在 Kubernetes 等平台上&#xff09;&#xff0c;以推动增长。 无论我们谈论可扩展性、效率还是灵活性&#xff0c;对于努力提供无与伦比的用…

源码|批量执行invokeAll()多选一invokeAny()

ExecutorService中定义了两个批量执行任务的方法&#xff0c;invokeAll()和invokeAny()&#xff0c;在批量执行或多选一的业务场景中非常方便。invokeAll()在所有任务都完成&#xff08;包括成功/被中断/超时&#xff09;后才会返回&#xff0c;invokeAny()在任意一个任务成功&…

校园博客系统 |基于springboot框架+ Mysql+Java的校园博客系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 系统功能设计 数据库E-R图设计 lunwen参考 摘要 研究…

MySQL语法分类 DDL(1)

DDL&#xff08;1&#xff09;(操作数据库、表) 数据库操作(CRUD) C(Create):创建 //指定字符集创建 create database db_1 character set utf8;//避免重复创建数据库报错可以用一下命令 create database if not exists db_1 character set utf8;R(Retrieve):查询 //查询所…

电源适配器

电源适配器 1. 选购指南2. 接口测量方法3. 电源接口4. 抗干扰磁环&#xff0c;稳定输出References 1. 选购指南 插头尺度相同&#xff0c;供电电压 (V) 相同&#xff0c;电流 (A) > 原来的电流 (A) INPUT (输入)&#xff0c;OUTPUT (输出) 2. 接口测量方法 3. 电源接口 外…

ARM和AMD介绍

一、介绍 ARM 和 AMD 都是计算机领域中的知名公司&#xff0c;它们在不同方面具有重要的影响和地位。 ARM&#xff08;Advanced RISC Machine&#xff09;&#xff1a;ARM 公司是一家总部位于英国的公司&#xff0c;专注于设计低功耗、高性能的处理器架构。ARM 架构以其精简指…

HCIP—BGP邻居关系建立实验

BGP的邻居称为&#xff1a;IBGP对等体 EBGP对等体 1.EBGP对等体关系&#xff1a; 位于 不同自治系统 的BGP路由器之间的BGP对等体关系 EBGP对等体一般使用 直连建立 对等体关系&#xff0c;EBGP邻居之间的报文 TTL中值设置为1 两台路由器之间建立EBGP对等体关系&#xff0…

Python Web开发记录 Day12:Django part6 用户登录

名人说&#xff1a;东边日出西边雨&#xff0c;道是无晴却有晴。——刘禹锡《竹枝词》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 1、登录界面2、用户名密码校验3、cookie与session配置①cookie与session②配置…

【机器学习-02】矩阵基础运算---numpy操作

在机器学习-01中&#xff0c;我们介绍了关于机器学习的一般建模流程&#xff0c;并且在基本没有数学公式和代码的情况下&#xff0c;简单介绍了关于线性回归的一般实现形式。不过这只是在初学阶段、为了不增加基础概念理解难度所采取的方法&#xff0c;但所有的技术最终都是为了…