读书笔记:java多线程之线程同步

阅读的书籍:《java疯狂讲义》

关键词:线程安全问题,同步代码块,同步方法,释放同步监视器的锁定,同步锁,死锁

线程安全问题:当使用多个线程来访问同一个数据时,会导致一些错误情况的发生

到底什么是线程安全问题呢,先看一个经典的案例:银行取钱的问题

 

模拟步骤:

1.匹配用户账户的正确性(这里就简化了)

2.用户输入取款金额

3.系统判断账户余额是否大于取款金额

4.返回取款成功或者失败

              

案例中一共就三个类,首先是Account:

/**
 * 银行账户
 */
public class Account {

    private String accountNo;//账户名
    private double balance;//账户余额

    public Account(String accountNo,double balance){
        this.accountNo = accountNo;
        this.balance = balance;
    }

    @Override
    public int hashCode() {
        return accountNo.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if(obj == this){
            return true;
        }

        if (obj != null && obj.getClass() == Account.class){
            Account target = (Account) obj;
            return target.getAccountNo().equals(accountNo);
        }

        return false;

    }

    public String getAccountNo() {
        return accountNo;
    }

    public void setAccountNo(String accountNo) {
        this.accountNo = accountNo;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}

 然后是DrawThread:

/**
 * 模拟取钱过程的线程
 */
public class DrawThread extends Thread {

    private Account mAccount;//账户信息
    private double mDrawAmount;//要取的金额

    public DrawThread(String name, Account account, double drawAmount) {
        super(name);
        mAccount = account;
        mDrawAmount = drawAmount;
    }

    @Override
    public void run() {

        if (mAccount.getBalance() >= mDrawAmount) {//如果 余额 >= 要取的数额,则进入取钱流程
            System.out.println(getName() + "取钱成功,取出金额:" + mDrawAmount);
            mAccount.setBalance(mAccount.getBalance() - mDrawAmount);//更新账户余额
            System.out.println("账户余额:" + mAccount.getBalance());
        } else {
            System.out.println(getName() + "取钱失败,余额不足");
        }
    }
}

最后用DrawThread进行测试:

public class DrawTest {

    public static void main(String[] args){
        Account account = new Account("王尼玛",1000);
        new DrawThread("小偷甲",account,800).start();
        new DrawThread("小偷乙",account,800).start();
    }

}

如果只是按上面的代码来运行的话,运行结果是这样的:

可以看到,这里就存在着一个问题:一开始账户中有1000块钱,小偷甲取走800后剩下200,若小偷乙继续取的话余额就不够了,可是这里却没有返回 “取钱失败,余额不足”,而是输出了一个负数:-600。两个并发线程同时在修改Account对象,出现了问题。

这就是线程安全问题的一个体现。那么如何解决呢?——使用同步监视器

同步代码块:使用同步监视器的通用方法就是同步代码块

语法格式:

synchronized(obj){

         //需要同步的代码块

}

线程在开始执行同步代码块之前,必须先获得对同步监视器的锁定。

任何时刻只能由一个线程可以获得对同步监视器的锁定,当同步代码块执行完成之后,该线程会释放对该同步监视器的锁定

 

明白了这个原理之后,对DrawThread中的run()方法小改一下:

@Override
    public void run() {

        synchronized (mAccount) {//看这里
            if (mAccount.getBalance() >= mDrawAmount) {//如果 余额 >= 要取的数额,则进入取钱流程
                System.out.println(getName() + "取钱成功,取出金额:" + mDrawAmount);
                mAccount.setBalance(mAccount.getBalance() - mDrawAmount);//更新账户余额
                System.out.println("账户余额:" + mAccount.getBalance());
            } else {
                System.out.println(getName() + "取钱失败,余额不足");
            }
        }
    }

可以看到,此时取钱的这块逻辑就是同步代码块,mAccount就是同步监视器了,取钱就是对mAccount进行操作,而经过这么一包裹之后,对于同一个账户来说,同一时间就只能有一个人对它进行取钱的操作了。再看看此时输出的结果:

嗯,小偷乙这会儿就取钱失败了,问题终于得到了解决。

同步方法:使用synchronized修饰某个方法来达到多线程安全

同步方法的同步监视器是调用该方法的对象,通过使用同步方法可以很方便地实现线程安全的类

线程安全的类的特点:

  • 该类的对象可以被多个线程安全地访问
  • 每个线程调用该对象的任意方法之后都将得到正确的结果,并且该对象状态依然保持合理状态

用同步方法对案例代码也进行小改:

    //在Account类中加入这个方法
    public synchronized void draw(double drawAmount){

        if (getBalance() >= drawAmount) {//如果 余额 >= 要取的数额,则进入取钱流程
            System.out.println(Thread.currentThread().getName() + "取钱成功,取出金额:" + drawAmount);
            setBalance(getBalance() - drawAmount);//更新账户余额
            System.out.println("账户余额:" + getBalance());
        } else {
            System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足");
        }

    }

然后回到DrawThread中调用:

 @Override
    public void run() {
        mAccount.draw(mDrawAmount);
    }

此时输出结果也是正确的,就不再重复贴图了

释放同步监视器的锁定:线程会在如下几种情况释放对同步监视器的锁定

  • 当前线程的同步代码块,同步方法正常执行结束
  • 遇到break,return终止了该同步代码块,同步方法的执行
  • 出现未处理的Error或Exception,该方法异常结束
  • 程序执行了同步监视器对象的wait()方法

与释放对应的是,当线程执行同步代码块,同步方法时,如下几种情况线程是不会释放同步监视器的:

  • 程序调用Thread.sleep(),Thread.yield()
  • 其他程序调用了该线程的suspend()方法

同步锁:通过显示定义同步锁对象(Lock)来实现同步

Lock是控制多个线程对共享资源进行访问的工具,通常每次只能由一个线程对Lock对象加锁,线程开始访问共享资源之前先要获得Lock对象

之所以说通常,是因为某些锁可能允许对共享资源并发访问,如ReadWriteLock

在实现线程安全的控制中,比较常用的是ReentrantLock(可重入锁)

死锁:当两个线程互相等待对方释放同步监视器时就会发生死锁,导致所有线程处于阻塞状态,无法继续

 由于篇幅有限,关于死锁就不展开了,后面会专门写一篇关于死锁的笔记或文章

注意事项:

  1. synchronized关键字可以修饰方法和代码块,但不能修饰构造器和成员变量等
  2. Thread类的suspend()方法很容易导致死锁,故Java不推荐使用这个方法

 

 

 

 

 

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下 4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
学校大创竞赛管理系统,学生上报项目内容,学院、教务处、评审专家评审。SpringBoot、SpringCloud、SpringSecurity、redis、Mysql、swagger、fastdfs、maven、vue、webpack.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值