zoukankan      html  css  js  c++  java
  • java进阶(38)--线程安全

    文档目录:

    一、概念

    二、解决方案

    三、举例说明

    ---------------------------------------分割线:正文--------------------------------------------------------

    一、概念

    关注数据在多线程并发时安全问题,共享数据有修改的行为。

    二、解决方案

    1、线程排队执行,不能并发,即线程同步机制。

    2、使用synchronized(){}线程同步代码块,()内填写需要同步的共享对象

    3、局部变量永远不存在线程安全问题,因为局部变量是不会共享的

    三、举例说明

    1、编写程序模拟两个线程同时对同一个账户进行去取款操作

    (1)变成账户类

     1 package JAVAADVANCE;
     2 
     3 public class Account {
     4     //账户与余额
     5     private String actNo;
     6     private double balance;
     7 
     8     public Account(String actNo, double balance) {
     9         this.actNo = actNo;
    10         this.balance = balance;
    11     }
    12 
    13     public String getActNo() {
    14         return actNo;
    15     }
    16 
    17     public void setActNo(String actNo) {
    18         this.actNo = actNo;
    19     }
    20 
    21     public double getBalance() {
    22         return balance;
    23     }
    24 
    25     public void setBalance(double balance) {
    26         this.balance = balance;
    27     }
    28     //取款
    29     public void withdraw(double money){
    30         //取款之前的余额
    31         double before=this.getBalance();
    32         //取款之后的余额
    33         double after = before-money;
    34         //更新余额
    35         this.setBalance(after);
    36     }
    37 
    38 }

    (2)编写账户线程类

     1 package JAVAADVANCE;
     2 
     3 public class AccountThread extends Thread {
     4     //两个线程必须共享同一个账户对象
     5     private Account act;
     6     //通过构造方法传递过来的账户对象
     7     public AccountThread(Account act){
     8         this.act=act;
     9     }
    10     @Override
    11     public void run() {
    12         //run方法执行表示取款操作
    13         //假设取款5000
    14         double money=5000;
    15         //取款
    16         act.withdraw(money);
    17         System.out.println(Thread.currentThread().getName()+"对账户"+act.getActNo()+"取款成功,余额为:"+act.getBalance());
    18     }
    19 }

    (3)测试类进行取款操作

     1 package JAVAADVANCE;
     2 
     3 public class TestAdvance38TestThreadSafe01 {
     4     public static void main(String[] args) {
     5         //创建账户对象,只创建一个
     6         Account act=new Account("act-001",10000);
     7         //创建两个线程
     8         Thread t1=new AccountThread(act);
     9         Thread t2=new AccountThread(act);
    10         //设置name
    11         t1.setName("t1");
    12         t2.setName("t2");
    13         //启动线程取款
    14         t1.start();
    15         t2.start();
    16     }
    17 
    18 }

    (4)查看运行结果,一定几率出现钱被取完,余额未更新,出现线程安全问题

    t2对账户act-001取款成功,余额为:5000.0
    t1对账户act-001取款成功,余额为:5000.0

    2、改进代码,使用线程同步机制来解决以上问题

     (1)加入synchronized ()线程同步代码块

    ()内需要填写多线程共享的数据,才能达到多线程排队。

     1     //取款
     2     public void withdraw(double money){
     3         //增加线程同步机制,里面是线程同步代码块
     4         synchronized (this){
     5             //取款之前的余额
     6             double before=this.getBalance();
     7             //取款之后的余额
     8             double after = before-money;
     9             try {
    10                 Thread.sleep(1000);
    11             } catch (InterruptedException e) {
    12                 e.printStackTrace();
    13             }
    14             //更新余额
    15             this.setBalance(after);
    16         }
    17 
    18     }

    改进后再次执行,多线程不会并发

    t1对账户act-001取款成功,余额为:5000.0
    t2对账户act-001取款成功,余额为:0.0

    (2)whithdraw调用时候增加,synchronized ()线程同步代码块,扩大了同步范围,执行效率变低

     1     @Override
     2     public void run() {
     3         //run方法执行表示取款操作
     4         //假设取款5000
     5         double money=5000;
     6         //取款
     7         synchronized (act){
     8         act.withdraw(money);}
     9         System.out.println(Thread.currentThread().getName()+"对账户"+act.getActNo()+"取款成功,余额为:"+act.getBalance());
    10     }

    执行效果同上:多线程不会并发

    (3)将实例方法使用synchronized

    缺点:一定需要锁this,整个方法体都需要同步,可能会扩大同步范围导致程序效率过低

    有点:代码比较少,简介

     1     //取款
     2     public synchronized void withdraw(double money){
     3         //增加线程同步机制,里面是线程同步代码块
     4 //        synchronized (this){
     5             //取款之前的余额
     6             double before=this.getBalance();
     7             //取款之后的余额
     8             double after = before-money;
     9             try {
    10                 Thread.sleep(1000);
    11             } catch (InterruptedException e) {
    12                 e.printStackTrace();
    13             }
    14             //更新余额
    15             this.setBalance(after);
    16         }
  • 相关阅读:
    作业8: 软件工程学习总结
    用户体验——南通大学教务学生管理系统
    “构建之法互动游戏”感想
    第二次作业
    音乐播放器的发展演变
    C++用法的学习心得
    一、最后一次作业:软件工程学习总结
    设计一款给爸爸妈妈用的手机
    附加题1—— 我想搞懂的软工问题
    计算机病毒软件的发展演变
  • 原文地址:https://www.cnblogs.com/mrwhite2020/p/14608861.html
Copyright © 2011-2022 走看看