清华扫地僧整理的全网最全多线程详解,看完怀疑自己的认知( 四 )

现在我们来分析一下以上代码:
我们现在希望实现的操作是模拟多个用户同时从银行账户里面取钱,如果用户取钱数小于等于当前账户余额,则提示取款成功,并将余额减去取款钱数,如果余额不足,则提示余额不足,取款失败 。
Account 类:银行账户类,里面有一些账户的基本信息,以及操作账户信息的方法
DrawThread类:继承了Thread,是一个多线程类,用于模拟多个用户操作同一个账户的信息
DrawTest:测试类
这时我们运行程序可能会看到如下运行结果:
甲取钱成功 800.0乙取钱成功 800.0余额为 200.0余额为 -600.0如何解决线程安全问题:①、同步代码块:
为了解决线程问题,Java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块 。同步代码块的语法格式如下:
synchronized(obj){
//此处的代码就是同步代码块
}
我们将上面银行中DrawThread类作如下修改:
import com.alibaba.util.Account; 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;}//多个线程修改同一个共享数据,可能发生线程安全问题@Overridepublic void run() {//使用account作为同步监视器,任何线程在进入下面同步代码块之前//必须先获得account账户的锁定,其他线程无法获得锁,也就无法修改它//这种做法符合:"加锁-修改-释放锁"的逻辑synchronized(account) {if(account.getBalance()>drawAmount) {System.out.println(getName()+"取钱成功"+" "+drawAmount);try {Thread.sleep(1);}catch(Exception e) {e.printStackTrace();}account.setBalance(account.getBalance()-drawAmount);System.out.println("t余额为"+" "+account.getBalance());}else {System.out.println("余额不足,取钱失败");}}}}我们来看这次的运行结果:
甲取钱成功 800.0余额为 200.0余额不足,取钱失败我们发现结果变了,是我们希望看到的结果 。因为我们在可能发生线程安全问题的地方加上了synchronized代码块
②:同步方法:
与同步代码块对应,Java的多线程安全支持还提供了同步方法,同步方法就是使用 synchronized关键字来修饰某个方法,则该方法称为同步方法 。对于 synchronized修饰的实例方法(非 static方法)而言,无须显式指定同步监视器,同步方法的同步监视器是this,也就是调用该方法的对象 。同步方法语法格式如下:
public synchronized void 方法名(){
//具体代码
}
③、同步锁:
从Java5开始,Java提供了一种功能更强大的线程同步机制—一通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当 。
Lock提供了比 synchronized方法和 synchronized代码块更广泛的锁定操作,Lock允许实现更灵活的结构,可以具有差别很大的属性,并且支持多个相关的 Condition对象 。
在实现线程安全的控制中,比较常用的是 ReentrantLock(可重入锁) 。使用该Lock对象可以显式加锁、释放锁,通常使用ReentrantLock的代码格式如下:
class X{//定义锁对象private final ReentrantLock lock=new ReentrantLock();//...//定义需要保护线程安全的方法public void m() {//加锁lock.lock();try {//需要保证线程安全的代码//...method body}finally {//释放锁lock.unlock();}}}死锁:
当两个线程相互等待对方释放同步监视器时就会发生死锁,Java虚拟机没有监测,也没有采取措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁岀现 。一旦岀现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续 。
死锁是很容易发生的,尤其在系统中出现多个同步监视器的情况下,如下程序将会出现死锁
class A{public synchronized void foo(B b) {System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入A实例的foo方法");//①try {Thread.sleep(200);}catch(InterruptedException e) {e.printStackTrace();}System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用B的方法");//③b.last();}public synchronized void last() {System.out.println("进入了A类的last方法");}}class B{public synchronized void bar(A a) {System.out.println("当前线程名:"+Thread.currentThread().getName()+"进入B实例的bar方法");//②try {Thread.sleep(200);}catch(InterruptedException e) {e.printStackTrace();}System.out.println("当前线程名:"+Thread.currentThread().getName()+"企图调用A的方法");//④a.last();}public synchronized void last() {System.out.println("进入了B类的last方法");}} public class DeadLock implements Runnable {A a=new A();B b=new B();public void init() {Thread.currentThread().setName("主线程");a.foo(b);System.out.println("进入了主线程之后");}@Overridepublic void run() {Thread.currentThread().setName("副线程");b.bar(a);System.out.println("进入副线程之后");}public static void main(String[] args) {DeadLock d=new DeadLock();new Thread(d).start();d.init();}}


推荐阅读