java--同步函数

通过一个实例,在多线程情况下去分析安全隐患并解决。

需求分析:有两个储户去银行存钱,每次存100,共存三次。

class Bank
{ 
	private int sum; //银行共有多少钱
	public void add(int num)
	{
		sum=sum+num;
		System.out.println("sum="+sum);
	}
}

class Customer implements Runnable  //顾客存钱的行为可以封装为线程任务
{
	public void run()
	{
		Bank b=new Bank();
		for(int x=0;x<3;x++)
		{
			b.add(100); //add()是Bank类对象的方法,所以要创建一个Bank类对象
		}
	}
}

class BankDemo
{
	public static void main(String[] args)
	{
		Customer c=new Customer();  //创建任务对象
		Thread t1=new Thread(c);    //创建线程
		Thread t2=new Thread(c);

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

运行结果:


我们看到,sum=100出现两次,sum=200出现两次,而两位顾客每一次存钱100元都会导致sum增加100,即运行结果发生错误。这是因为我们在Customer类的run()方法中创建了一个Bank类对象,而两个线程(顾客)在每次执行任务代码(存钱)时都会重新创建Bank()对象,实则就创建了两个Bank类对象,而并分操作同一个银行。在Customer类中应该这样编写:

class Customer implements Runnable  //顾客存钱的行为可以封装为线程任务
{
	private Bank b=new Bank();  //保证多个线程操作同一个Bank类对象
	public void run()
	{
		
		for(int x=0;x<3;x++)
		{
			b.add(100); //add()是Bank类对象的方法,所以要创建一个Bank类对象
		}
	}
}
运行结果:

现在显示的结果是正确的,但在现在的程序中仍然存在线程安全隐患。

通过分析,Bank类的b对象和Bank类的sum成员都是多线程的共享数据,Bank类的add()方法是线程任务代码。在本例中,对共享数据的操作不止一条,就可能存在安全问题。我们调用sleep()方法来验证这个问题:

class Bank
{ 
	private int sum; //银行共有多少钱
	public void add(int num)
	{
		sum=sum+num;
		try{Thread.sleep(10);}catch(InterruptedException e){}
		System.out.println("sum="+sum);
	}
}
运行结果:

出现了两次sum=200,sum=400,sum=600,发生了安全问题。


所以用同步代码块将线程代码进行封装:

class Bank
{ 
	private int sum; 
	private Object obj=new Object();
	public void add(int num)
	{
		synchronized(obj) //此时不能直接使用new Object()来创建对象,原理如上所讲,即每个线程使用自己的锁。
		{
			sum=sum+num;
			try{Thread.sleep(10);}catch(InterruptedException e){}
			System.out.println("sum="+sum);
		}
	}
}

运行结果:




同步函数:在上面的代码中,函数add()和synchronized都对代码进行了封装,但synchronized是带有同步特性的封装。让函数具备同步性就解决了上述问题。就是将同步关键字作为函数的修饰符:

class Bank
{ 
	private int sum; 
	public synchronized void add(int num) //同步函数-->解决线程安全问题
	{			
		sum=sum+num;
		try{Thread.sleep(10);}catch(InterruptedException e){}
		System.out.println("sum="+sum);
		
	}
}
运行结果:

注:同步函数使用的锁是this。


引申:1.同步函数和同步代码块的区别:同步函数的锁是固定的this(当前对象),同步代码块的锁是任意的对象。

2.静态的同步函数使用的锁是,该函数所属字节码文件对象,可以用 getClass()方法获取,也可以用当前  类名.class 表示。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值