Skip to content

Latest commit

 

History

History
307 lines (241 loc) · 5.93 KB

3-3-案例-银行存钱同步函数.md

File metadata and controls

307 lines (241 loc) · 5.93 KB
layout title date updated tags categories permalink thumbnail toc comment notag top
3-3-案例-银行存钱&同步函数
2015-10-09
2015-10-09
Java多线程
Java多线程基础
Java多线程案例
Java多线程基础
true
true
false
false

银行存钱案例

  • 两个客户到一个银行去存钱,每个客户一次存100,存三次。
  • 问题:改程序是否有安全问题,如果有写出分析过程并定义解决方案

代码示例-未修改前

代码

class Bank{
    private int sum;
    private Object obj = new Object();
    public void add(int num)
    {
        sale(num);
    }

    /**
     * 需要同步的同步函数
     * @param num
     */
    private /*synchronized*/ void sale(int num)
    {
        sum = sum+num;
        /* 此处可能发生CPU切换 */
        System.out.println("sum " + sum);
    }
}
class Consumer implements Runnable{
    private Bank bank = new Bank();
    public void run() {
        for (int i = 0 ; i < 3 ; i ++)
        {
            bank.add(100);
        }
    }
}

public class ThreadDemo{
    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        Thread t1 = new Thread(consumer);
        Thread t2 = new Thread(consumer);

        t1.start();
        t2.start();
    }
}

运行结果

sum 200
sum 200
sum 300
sum 400
sum 500
sum 600

分析

问题来源

  • 既然是多线程问题,必然发生在线程任务内
  • 任务代码中是否有共享数据呢? 操作此数据的代码时候有多条任务呢?
private Bank bank = new Bank();
public void run() {
    for (int i = 0 ; i < 3 ; i ++)
    {
        bank.add(100);
    }
}

private int sum;

private /*synchronized*/ void sale(int num)
{
    sum = sum+num;
         /* 此处可能发生CPU切换 */
    System.out.println("sum " + sum);
}

bank是共享数据,具体的讲,bank中的sum是共享数据,并且有多条代码操作此数据.

所以我们需要给操作共享变量的函数加同步

程序示例

class Bank{
    private int sum;
    private Object obj = new Object();
    public void add(int num)
    {
        sale(num);
    }

    /**
     * 同步函数
     * @param num
     */
    private synchronized void sale(int num)
    {
        sum = sum+num;
        /* 此处不再发生CPU切换 因为其他线程得不到锁*/
        System.out.println("sum " + sum);
    }
}
class Consumer implements Runnable{
    private Bank bank = new Bank();
    public void run() {
        for (int i = 0 ; i < 3 ; i ++)
        {
            bank.add(100);
        }
    }
}

public class ThreadDemo{
    public static void main(String[] args) {
        Consumer consumer = new Consumer();
        Thread t1 = new Thread(consumer);
        Thread t2 = new Thread(consumer);

        t1.start();
        t2.start();
    }
}

 同步函数

  • 同步的另外一个体现形式:同步函数 private synchronized void sale(int num)
  • 同步函数使用this做锁 , 因为函数必须被对象调用 (可写一个同步代码块,写一个同步函数,同步代码块以this为锁,如果同步代码块中锁对象是同一个 ,那么这一块代码不可被同步执行.)

class Ticket implements Runnable
{
	private int tickets = 100;
	private Object obj = new Object();
	boolean flag = true;
	public void run()
	{
		if(flag){
			while(true){
				synchronized(this){
					if(tickets>0){
						try{Thread.sleep(10);}catch(InterruptedException e){}
						System.out.println(Thread.currentThread().getName()+"...obj..."+tickets--);//打印线程名称。
					}
				}
			}
		}
		else{
			while(true){
				this.sale();
			}
		}
	}

	public synchronized void sale()//同步函数,使用的锁对象 this。
	{
		if(tickets>0)
		{
			try{Thread.sleep(10);}catch(InterruptedException e){}
			System.out.println(Thread.currentThread().getName()+"...sale..."+tickets--);//打印线程名称。
		}
	}
}
class ThreadDemo4 
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);

		t1.start();
		try{Thread.sleep(10);}catch(InterruptedException e){}
		//切换标记,之前,让主线程停一会,这时就只有一个t1线程在,它就会执行同步代码块。
		t.flag = false;

		t2.start();
	}
}

同步函数和同步代码块有什么区别

  • 同步函数使用的锁是固定的this。当线程任务中只需要一个同步时完全可以使用同步函数
  • 同步代码块使用的锁是任意的对象。当线程任务中需要多个同步时,必须通过锁来区分
  • 静态同步函数使用的锁不是this而是字节码文件对象 类名.class (一般当只有一个锁时只用同步函数就可以)

静态同步函数的锁

静态同步函数使用.class对象做锁



/*
static 同步函数,使用的锁不是this,而是字节码文件对象, 类名.class   
*/
class Ticket implements Runnable
{
	private static int tickets = 100;
	private Object obj = new Object();
	boolean flag = true;
	public void run()
	{
		if(flag){
			while(true){
				synchronized(Ticket.class){
					if(tickets>0){
						try{Thread.sleep(10);}catch(InterruptedException e){}
						System.out.println(Thread.currentThread().getName()+"...obj..."+tickets--);//打印线程名称。
					}
				}
			}
		}
		else{
			while(true){
				this.sale();
			}
		}
	}

	public static synchronized void sale()//
	{
		if(tickets>0)
		{
			try{Thread.sleep(10);}catch(InterruptedException e){}
			System.out.println(Thread.currentThread().getName()+"...sale..."+tickets--);//打印线程名称。
		}
	}
}
class ThreadDemo5 
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);

		t1.start();
		try{Thread.sleep(10);}catch(InterruptedException e){}
		//切换标记,之前,让主线程停一会,这时就只有一个t1线程在,它就会执行同步代码块。
		t.flag = false;

		t2.start();
	}
}