- 由于多线程是并发执行的,这使得线程调度具有一定的随机性,一个线程执行到一半可能会被暂停。又由于线程是共享父进程的资源的,这就使得可能两个线程对一个变量的并发访问使得多线程的执行结果会出现意想不到的结局
- 例如线程a,b需要对成员变量A进行访问然后修改,a在访问后A然后还没有修改的时候线程b也对A进行了访问,这就可能会发生矛盾
- 下面这个例子就说明了上述的问题,甲进程在取了钱,并且显示取钱成功但是系统还没有对余额进行减少的时候乙取了钱,,并且显示取钱成功,然后再执行甲进程对金额的减少,再执行乙对金额的减少,虽然这只是只是有可能会发生的情况,但是这即体现出了程序的健壮性不达标,具体的讲解穿插再代码中
//账号类,包含了账号和余额两个成员变量 public class Account { private String accountNo; private double balance; public Account(){} public Account(String accountNo, double balance){ this.accountNo = accountNo; this.balance = balance; } 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; } public int hashCode(){ return accountNo.hashCode(); } public boolean equals(Object obj){ if(this == obj){ return true; } if(obj != null && obj.getClass() == Account.class){ Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false; } }
//“取钱”类,取钱的行为是在线程中的 public class DrawThread extends Thread{ private Account account; private double drawAmount; public DrawThread(String name, Account account, double drawAmount){ super(name); this.account = account; this.drawAmount = drawAmount; } // 在run方法中,对余额进行了判断,在余额大于取钱的金额的时候才执行取钱操作,但是在多线程中 // 由于甲线程在取钱之后还没有来得及对余额进行修改,乙又来进行取钱了,然后有进行了甲和乙对余额的 // 修改,虽然这只是有可能发生的情况 public void run() { if (account.getBalance() >= drawAmount) { System.out.println(getName() + "取钱成功!吐出钞票:" + drawAmount); // try{ // Thread.sleep(1); // }catch (InterruptedException e){ // e.printStackTrace(); // } account.setBalance(account.getBalance() - drawAmount); System.out.println("\t余额:" + account.getBalance()); } else { System.out.println(getName() + "取钱失败,余额不足"); } } }
//执行类 public class DrawTest { public static void main(String args[]){ Account account = new Account("1234567", 1000); new DrawThread("甲", account, 800).start(); new DrawThread("乙", account, 800).start(); } } //执行这个类之后,有可能会出现以下的结果,原因在DrawThread类中给出: // 乙取钱成功!吐出钞票:800.0 // 余额:200.0 // 甲取钱成功!吐出钞票:800.0 // 余额:-600.0
-
这个时候就需要对想成进行同步化,线程同步化可以通过synchronized(){}方法来实现同步代码块,这个方法将会对放在圆括号里的对象进行锁定,使得一个线程对圆括号里的对象进行完花括号里的操作之前,别的线程不会获得锁,也就不能对这个对象进行操作。在这个例子中,在同步化之后,在一个线程没有执行完同步化的所有操作之前,别的线程不会获得执行的机会,代码如下
//“取钱”类,取钱的行为是在线程中的 public class DrawThread extends Thread{ private Account account; private double drawAmount; public DrawThread(String name, Account account, double drawAmount){ super(name); this.account = account; this.drawAmount = drawAmount; } // 在run方法中,对余额进行了判断,在余额大于取钱的金额的时候才执行取钱操作,但是在多线程中 // 由于甲线程在取钱之后还没有来得及对余额进行修改,乙又来进行取钱了,然后有进行了甲和乙对余额的 // 修改,虽然这只是有可能发生的情况 public void run() { synchronized (account) { if (account.getBalance() >= drawAmount) { System.out.println(getName() + "取钱成功!吐出钞票:" + drawAmount); // try{ // Thread.sleep(1); // }catch (InterruptedException e){ // e.printStackTrace(); // } account.setBalance(account.getBalance() - drawAmount); System.out.println("\t余额:" + account.getBalance()); } else { System.out.println(getName() + "取钱失败,余额不足"); } } } }
还有可以通过同步方法来实现,同步方法是直接在方法的名字之前加synchronized关键字修饰,代码如下
//账号类,包含了账号和余额两个成员变量 public class Account { private String accountNo; private double balance; public Account() { } public Account(String accountNo, double balance) { this.accountNo = accountNo; this.balance = balance; } 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; } public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if (this == obj) { return true; } if (obj != null && obj.getClass() == Account.class) { Account target = (Account) obj; return target.getAccountNo().equals(accountNo); } return false; } public synchronized void draw(double drawAmount) { if (balance >= drawAmount) { System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:" + drawAmount); // try{ // Thread.sleep(1); // }catch (InterruptedException e){ // e.printStackTrace(); // } balance -= drawAmount; System.out.println("\t余额:" + balance); } else { System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足"); } } }
//“取钱”类,取钱的行为是在线程中的 public class DrawThread extends Thread{ private Account account; private double drawAmount; public DrawThread(String name, Account account, double drawAmount){ super(name); this.account = account; this.drawAmount = drawAmount; } // 在run方法中,对余额进行了判断,在余额大于取钱的金额的时候才执行取钱操作,但是在多线程中 // 由于甲线程在取钱之后还没有来得及对余额进行修改,乙又来进行取钱了,然后有进行了甲和乙对余额的 // 修改,虽然这只是有可能发生的情况 public void run() { account.draw(drawAmount); // synchronized (account) { // if (account.getBalance() >= drawAmount) { // System.out.println(getName() + "取钱成功!吐出钞票:" + drawAmount); try{ Thread.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); } // account.setBalance(account.getBalance() - drawAmount); // System.out.println("\t余额:" + account.getBalance()); // } else { // System.out.println(getName() + "取钱失败,余额不足"); // } } } }
还有一种方法是使用同步锁,每次只能有一个线程对同步锁对象加锁,线程开始访问共享资源之前应当先获得锁对象,具体讲解穿插在代码中
//在程序中先定义了一个同步锁对象,在实现draw方法的时候,进入方法在还没有执行具体的取钱操作之前先请求对同步锁对象 //进行加锁,在执行完取钱操作之后,程序在finally中释放同步锁 import java.util.concurrent.locks.ReentrantLock; //账号类,包含了账号和余额两个成员变量 public class Account { private String accountNo; private double balance; private final ReentrantLock lock = new ReentrantLock(); // 定义一个锁对象 public Account() { } public Account(String accountNo, double balance) { this.accountNo = accountNo; this.balance = balance; } 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; } public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if (this == obj) { return true; } if (obj != null && obj.getClass() == Account.class) { Account target = (Account) obj; return target.getAccountNo().equals(accountNo); } return false; } public void draw(double drawAmount) { lock.lock(); // 这一步就是加锁操作,线程访问共享资源之前应当先获得锁对象 try{ if (balance >= drawAmount) { System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:" + drawAmount); // try{ // Thread.sleep(1); // }catch (InterruptedException e){ // e.printStackTrace(); // } balance -= drawAmount; System.out.println("\t余额:" + balance); } else { System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足"); } }finally { lock.unlock(); // 在访问完共享资源之后应当释放锁对象 } } }
java多线程(8)线程同步
最新推荐文章于 2022-08-26 11:03:01 发布