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); } }

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()); } }

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(); } }
可以看出会造成线程安全问题,而此时则需要修改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(); }*/ } }
明天学习一下死锁。