(47)同步函数,例子银行存款、对之前的卖票写同步函数

需求:银行有一个金库,有两个储户分别存300元,存3次。
目的:该程序是否有安全问题,若有,如何解决?

public class Bank {
    private int sum;
    public void add(int n) {
        sum=sum+n;
        System.out.println("sum="+sum);
    }
}

public class Cus implements Runnable {

    private Bank b=new Bank();
    public  void run() {
        for(int i=0;i<3;i++) {
            b.add(100);
        }
    }
}

public class BankDemo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Cus c=new Cus();
        Thread t1=new Thread(c);
        Thread t2=new Thread(c);
        t1.start();
        t2.start();

    }

}

一、如何找到问题呢?
1.明确哪些代码是多线程运行代码
2明确共享数据
3明确多线程运行代码中哪些语句是操作共享数据
二、同步实现的两种方式
①同步代码块(run方法内)
②同步函数(run方法上)
如何找到问题举例:
有两个共享数据:Bank类中sum和Cus类中的Bank。
若把Cus中的Bank设为同步,则成了单线程。因为同步中有个for,此同步结束,相当于一个客户结束,即单线程。
可以让多个客户去存,即多线程。但是sum这个必须得单线程,这个共享区得同步,否则会有问题。
【这个问题举例:第一个用户函数调用到add,执行完sum=sum+n;后,sum=100,但没有打印,CPU夺走,第二个用户调用add,执行sum=sum+n;,sum=200,也没打印,CPU夺走,假如此时第一个用户得到cpu,则打印了200,第一个用户的第一笔钱结束调用,数据错误。第二个用户此时获得cpu,打印200。】
所以必须给sum同步。
同步之后,执行是什么样的呢?
第一个用户调用到了add,现在锁是开的“1”,执行sum=sum+n;睡觉,释放cpu.比如此时第二个用户调用了add,此时锁是关的,所以得等第一个用户睡醒输出sum后释放锁,就实现了同步,不会存储错误数据。

修改1public class Bank {
    private int sum;
    Object obj=new Object();
    public void add(int n) {
        synchronized(obj) {
        sum=sum+n;
        try {
        Thread.sleep(10);
        }
        catch(Exception e) {}
        System.out.println("sum="+sum);
    }
    }

}

也可以对其写成同步函数public synchronized void add(int n) {},就不用再定义对象了。
但是呢,并不是所有的都可以直接就写成同步函数。
这个能够写成同步函数的原因是:执行add立刻就判断锁了。
例如之前(46)中的卖票,就不能直接写成同步函数。
比如直接写成同步函数,对其分析错误:

public class Demo implements Runnable{
    private int tick=10;
    public synchronized void run() {
        while(true) {

            if(tick>0) {
                try {
                    Thread.sleep(10);
                } catch (Exception e) {}



           System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
             }
            }
        }

}

不能在此run方法上写同步函数,
原因是第一个线程将锁置为0,一直true,一直打印,无法出同步。
想写同步函数,则将同步代码块封装成一个函数,然后调用

public class Demo implements Runnable{
    private int tick=10;
    Object obj=new Object();
    public void run() {
        while(true) {
           show();
            }
        }
    public synchronized void show() //这样就不会发生只有一个线程执行,因为执行完这个函数,打印一个数,就释放锁,其他的线程可以打印
    {
         if(tick>0) {
             try {
                 Thread.sleep(10);
             } catch (Exception e) {
                 // TODO Auto-generated catch block

             }
             System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
          }

     }


    }
posted @ 2017-07-12 14:38  测试开发分享站  阅读(102)  评论(0)    收藏  举报