Java 初学者-java线程安全问题

今天学习了java多线程的线程安全问题。

通过转账来演示线程安全问题。

//不使用线程同步机制,造成线程安全问题
public class Account {

    private String acnum;
    private double balance;
    
    public Account(String acnum, double balance) {
        super();
        this.acnum = acnum;
        this.balance = balance;
    }

    public String getAcnum() {
        return acnum;
    }

    public void setAcnum(String acnum) {
        this.acnum = acnum;
    }

    public double getBalance() {
        return balance;
    }

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

    public void withdraw(double mon) {
        //t1与t2并发这个方法(t1和t2是两个栈,两个栈操作堆中同一个方法)
        double b=this.getBalance();//这时候t2可能进来,而t1并未执行setBalance()方法,因此异常
        double after=b-mon;
        /*try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/
        this.setBalance(after);
    }
    
}
Account
public class AccountThread extends Thread {
  //两个线程必须共享同一个账户对象
    private Account a;
//通过构造方法传递账户对象
public AccountThread(Account a) {
    super();
    this.a = a;
}
public void run() {
    //执行取款操作
    double m=5000;
    a.withdraw(m);
    System.out.println(Thread.currentThread().getName()+"账户"+a.getAcnum()+" 余额"+a.getBalance());
}
}
AccountThread
public class Test {
  public static void main(String []args) {
      
      Account act=new Account("act-1",10000);
      Thread t1=new AccountThread(act);
      Thread t2=new AccountThread(act);
      
      t1.setName("t1");
      t2.setName("t2");
      t1.start();
      t2.start();
  }
}
View Code

可以看出会造成线程安全问题,而此时则需要修改Account方法来解决线程安全问题。

package ThreadSAFE2;
//不使用线程同步机制,造成线程安全问题
public class Account {

    private String acnum;
    private double balance;
    
    public Account(String acnum, double balance) {
        super();
        this.acnum = acnum;
        this.balance = balance;
    }

    public String getAcnum() {
        return acnum;
    }

    public void setAcnum(String acnum) {
        this.acnum = acnum;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
 
//在实例方法可以使用synchronzied,但一定锁的是this。
//因此不常使用。
//缺点:会扩大线程同步范围,导致程序执行效率变慢。
    
    
//    public synchronized void withdraw(double mon) {
    public void withdraw(double mon) {
        //synchronized的()写什么
        //看你想要让哪一个线程同步
        //假设5个线程,让1,2,3排队 4,5不排队
        //则写1,2,3,线程的共享对象
        
        //在java代码中,任何一个对象都有一把锁,这把锁就是标记。(只是叫做锁)
        //两个线程有一先一后。t1先进入,遇到synchronized,这时自动找"后面共享对象"的对象锁。
        //然后找到之后,并占有这把锁,然后执行同步代码的程序,在执行过程中,一直占有这把锁
        //直到同步代码块结束之后,这把锁才会释放。
        //但t2遇到synchronized后,因为t1已经占用后面的共享对象,因此t2遇到后面的关键字
        //只能等待到t1执行完毕后,t2才能进入
        //这样达到了线程同步执行。
        double after;
        //synchronized ("abx") {//可以,因为此对象放在字符串常量池里
        //synchronized (null) {空指针异常
        synchronized (this) {
            double b=this.getBalance();
             after=b-mon;
        /*    try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }*/
            this.setBalance(after);
        }
        /*try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/
        
    }
    
}
Account

明天学习一下死锁。

posted @ 2020-08-26 21:10  好吗,好  阅读(84)  评论(0)    收藏  举报