Java使用线程同步解决线程安全问题详解
作者:遇安.112 发布时间:2022-02-28 02:03:24
标签:Java,线程,安全,同步
第一种方法:同步代码块:
作用:把出现线程安全的核心代码上锁
原理:每次只能一个线程进入,执行完毕后自行解锁,其他线程才能进来执行
锁对象要求:理论上,锁对象只要对于当前同时执行的线程是同一个对象即可
缺点:会干扰其他无关线程的执行
所以,这种只是理论上的,了解即可,现实中并不会这样用
public class 多线程_4线程同步 {
public static void main(String[] args) {
//定义线程类,创建一个共享的账户对象
account a=new account("abc",10000);
//创建两个取钱的线程对象
new drawthread(a,"小明").start();
new drawthread(a,"小红").start();
}
}
//取钱的线程类
class drawthread extends Thread{
//接收处理的账户对象
private account acc;
public drawthread(account acc,String name){
super(name);
this.acc=acc;
}
public void run(){
//取钱
acc.drawmoney(10000);
}
}
class account{
private String cartId;
private double money;//账户余额
public account() {
}
public account(String cartId, double money) {
this.cartId = cartId;
this.money = money;
}
public String getCartId() {
return cartId;
}
public void setCartId(String cartId) {
this.cartId = cartId;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawmoney(double money) {
//先获取是谁来取钱,线程名即是人名
String name=Thread.currentThread().getName();
//同步代码块
//作用:把出现线程安全的核心代码上锁
//原理:每次只能一个线程进入,执行完毕后自行解锁,其他线程才能进来执行
//锁对象要求:理论上,锁对象只要对于当前同时执行的线程是同一个对象即可
//缺点:会干扰其他无关线程的执行
synchronized ("遇安") {//"锁名自取,无意义"
//判断账户是否够钱
if(this.money>=money){
//取钱
System.out.println(name+"来取钱成功,取了:"+money);
//更新金额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else{
//余额不足
System.out.println(name+"来取钱,但余额不足!");
}
}
}
}
规范上:建议使用共享资源作为锁对象
对于实例化方法建议使用this作为锁对象
对于静态方法,建议使用字节码(类名.class)对象作为锁对象
//接上文代码
//实例化方法建议使用this作为锁对象
synchronized (this) {
//判断账户是否够钱
if(this.money>=money){
//取钱
System.out.println(name+"来取钱成功,取了:"+money);
//更新金额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else{
//余额不足
System.out.println(name+"来取钱,但余额不足!");
}
}
//静态方法建议使用类名.class作为锁对象
//每次只有一个线程能锁这个类,而类也是唯一的
public static void run(){
synchronized(account.class){
}
}
第二种方法:同步方法
//同步方法
public synchronized void drawmoney(double money) {
//先获取是谁来取钱,线程名即是人名
String name=Thread.currentThread().getName();
//判断账户是否够钱
if(this.money>=money){
//取钱
System.out.println(name+"来取钱成功,取了:"+money);
//更新金额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else{
//余额不足
System.out.println(name+"来取钱,但余额不足!");
}
}
那么同步代码块和同步方法哪个好一点呢?
答案是:同步代码块
因为同步代码块锁的范围更小一点,同步方法锁的范围更大一点
但其实在现实中同步方法用的更多一点,因为代码简洁好写一点,更方便
第三种方法:Lock锁
JDK5后出现,更加灵活方便
Lock是接口不能直接实例化,我们需要采用它的实现类ReentrantLock来构建Lock锁对象
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class 多线程_4线程同步Lock锁 {
public static void main(String[] args) {
//定义线程类,创建一个共享的账户对象
account a=new account("abc",10000);
//创建两个取钱的线程对象
new drawthread(a,"小明").start();
new drawthread(a,"小红").start();
}
}
//取钱的线程类
class drawthread2 extends Thread{
//接收处理的账户对象
private account acc;
public drawthread2(account acc,String name){
super(name);
this.acc=acc;
}
public void run(){
//取钱
acc.drawmoney(10000);
}
}
class account2{
private String cartId;
private double money;//账户余额
//final修饰后:锁对象是唯一的和不可替换的
//Lock是接口不能直接实例化,我们需要采用它的实现类ReentrantLock来构建Lock锁对象
private final Lock lock=new ReentrantLock();
public account2() {
}
public account2(String cartId, double money) {
this.cartId = cartId;
this.money = money;
}
public String getCartId() {
return cartId;
}
public void setCartId(String cartId) {
this.cartId = cartId;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public void drawmoney(double money) {
//先获取是谁来取钱,线程名即是人名
String name=Thread.currentThread().getName();
lock.lock();//上锁
//判断账户是否够钱
try {
if(this.money>=money){
//取钱
System.out.println(name+"来取钱成功,取了:"+money);
//更新金额
this.money-=money;
System.out.println(name+"取钱后剩余:"+this.money);
}else{
//余额不足
System.out.println(name+"来取钱,但余额不足!");
}
//防止代码出现bug而不能解锁
} finally {
lock.unlock();//解锁
}
}
}
来源:https://blog.csdn.net/qq_62731133/article/details/124420818


猜你喜欢
- 布局文件:res/layout/activity_my.xml[html] <LinearLayout xmlns:and
- 本文实例分析了Java接口默认方法带来的问题。分享给大家供大家参考,具体如下:一 点睛Java 8中,如果一个类实现两个或多个接口,即“变相
- FeignClient脱离eureka自定义URL需求Spring Cloud环境中的FeignClient有时候需要调用特定主机的接口,但
- Android程序调用本机googlemap,传递起始和终点位置,生成路线图if (wodeweizhiPoint != null) { i
- mybatis的原身是ibatis,现在已经脱离了apache基金会,新官网是http://www.mybatis.org/。在研究Myba
- 本文分享了c#操作Excel的相关代码,还是比较全面的,其实无外乎存取,增删改查等操作,参考下。具体代码://引用Microsoft.Off
- 扫码抢实现读取二维码信息,本地扫码枪是外接写入设备,本质是监控读写输入,下面介绍下扫码设备读取支付二维码。1.引入扫码设备辅助类public
- MyBatis根据条件批量修改字段背景:给学生改作业,只要是对的都批量进行数据库的修改代码以及注释conttoller@RestContro
- 为Repository添加自定义方法一、为某个Repository添加自定义方法1、定义一个接口PersonDao,声明要添加的方法。pub
- 对谷歌地图操作使用的是WebBrowser控件,通过对javascript的操作来实现对谷歌地图的各种操作,所以首先要创建一个html文件,
- 一、错误处理原理分析使用SpringBoot创建的web项目中,当我们请求的页面不存在(http状态码为404),或者器发生异常(http状
- 一对多查询一对多关联查询是指在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。下面以班级 Classes 与学生 Student
- 1.什么是Spring Boot为什么要学Spring Boot?Spring 的诞生是为了简化 Java 程序的开发的, Spring B
- 题目一??解法/** * Definition for singly-linked list. * public class ListNod
- 具体详细介绍请看下文:在使用文件进行交互数据的应用来说,使用FTP服务器是一个很好的选择。本文使用Apache Jakarta Common
- 一、前言本博文标题和内容参考:基于原生JS实现H5转盘游戏博主将改编成Unity版本。二、效果图三、案例制作1.界面搭建使用了9个图片作为奖
- 引言容器是Java基础类库中使用频率最高的一部分,Java集合包中提供了大量的容器类来帮组我们简化开发,我前面的文章中对Java集合包中的关
- MyBatis 是一款常用的持久层框架,使得程序能够以调用方法的方式执行某个指定的SQL,将执行SQL的底层逻辑进行封装。多数与Spring
- 本文实例为大家分享了Mybatis分页插件使用的具体代码,供大家参考,具体内容如下1.分页插件简介pagehelper源码都说这是史上最好用
- springboot @ConfigurationProperties和@PropertySource区别@ConfigurationPro