欢迎来到代码驿站!

JAVA代码

当前位置:首页 > 软件编程 > JAVA代码

Java使用线程同步解决线程安全问题详解

时间:2022-09-15 08:56:42|栏目: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();//解锁
        }
    }
}

上一篇:java分布式缓存方案

栏    目:JAVA代码

下一篇:详解Spring系列之@ComponentScan批量注册bean

本文标题:Java使用线程同步解决线程安全问题详解

本文地址:http://www.codeinn.net/misctech/213697.html

推荐教程

广告投放 | 联系我们 | 版权申明

重要申明:本站所有的文章、图片、评论等,均由网友发表或上传并维护或收集自网络,属个人行为,与本站立场无关。

如果侵犯了您的权利,请与我们联系,我们将在24小时内进行处理、任何非本站因素导致的法律后果,本站均不负任何责任。

联系QQ:914707363 | 邮箱:codeinn#126.com(#换成@)

Copyright © 2020 代码驿站 版权所有