Java-初步认识-第十三章-同步函数

一.

现在用一个示例,将刚才所讲的同步内容练习一下。

对于一个函数中,存在多个线程,如果出现安全隐患,这隐患是在哪儿?以及如何通过同步函数来解决。(相当于一个实操)

存一百,存三次是共同的,两个人执行的是同样的动作。

线程开启之前,要先有任务。这个任务要么叫爹覆盖我的方法,要么实现Runable把你任务封装对象给传递过来。两种不同的操作方式,都是为了将需要执行的代码和线程的任务联系起来。

----------

对于书写的程序后面怎么运行,运行过程中涉及哪些问题,问题怎么处理的我都不是很关心。我关心的是如果将需求变成该程序的,这才是核心。

需求是:储户,两个,到银行存钱。每次存100,存三次。这里面的名词有三个,储户,银行,钱。首先可以明确的是,储户是可以单独成一类的,存钱的动作是要封装成任务对象。因为是两个储户在存钱,这是个多线程执行的程序。我觉着,存钱这个动作是钱自己知道,定义的类应该是钱的描述类,而不是银行的描述类。最后一个类是包含主函数的执行类。

----------

编译运行的结果如上图所示,sum是银行的金库。这里确实是开启了两个线程,而且线程之间是交替运行了。

这两个储户实际上是去了两个银行,而不是在一个银行的多个柜台。因为Bank b的创建是在run方法中,这时要修改程序,

这次的结果符合银行金库的数目跳动规则,在储户存钱时,金库数目始终是上涨的,打印结果有点乱是正常的。

现在就问,这个程序有没有安全隐患,有的话,怎么解决?

要想知道程序的安全隐患在哪儿?必须要知道刚才讲述的造成程序安全隐患的原因。那个原因里面涉及了几个要素。在线程运行的代码当中,是否有共享数据,这里的b就是共享数据。b.add(100),b调用add方法就一句,没事儿。首先,要搞清楚线程的代码有哪些?

正因为b的调用,导致add()方法也变成了run方法中的内容。run方法进栈后,add方法也进栈了,进的是同一个线程栈。add代码本身也属于线程的代码,因为它被我们所定义的线程运行到了。add代码运行的时候,sum就是共享数据。

对于第一点满足了,存在共享数据。第二个就是数据的代码操作不止一条啊。一条加法,一条输出,至少有两条。

有人说,一个加法,一个输出有问题吗?这就很难说了,

有一个线程进来了,num=100, sum=0+100=100,突然冻结了。这时又进来一个线程,num也是100,sum=100+100,输出了sum为200。然后接着执行第一条线程,输出的也是200。这就出现问题了,本来第一条线程应该输出100的。这就是安全隐患。

为了让安全隐患显现出来,修改一下程序。(使用sleep方法会出现异常,为了程序继续执行,采用try-catch处理)

DOS结果显示确实出现了安全隐患,为了解决这个问题,我们要对程序进行封装。

这时要考虑封装谁,安全问题出现在sum=sum+num,和输出语句之间。不能直接将所有语句放入同步函数中,这样是有问题的,一会儿演示。

有的人为了图省事,直接在括号里创建匿名对象,这也是错误的,因为这就不是共享对象了,而是每个线程一个对象了。这和刚才将创建对象放置在run方法中是一回事。这就成了每个人都有一个单独地锁么。

增加同步函数后,安全问题解决了,

这是第一步,我们拿着一个程序,试着找出线程安全隐患是在哪儿发生的。紧跟着又是如何去解决的。

现在开始讲述另外一部分,原先出问题的代码都属于add函数,而且函数本身就是一种封装体,同步代码块本身也是封装体。其实它俩都是封装体,哪儿不一样?一个是封装,一个是带有特性的封装,就是同步特效。那么,能不能借助原有函数辅以同步特效,实现解决安全隐患的作用。怎么做?将同步关键字作为函数的修饰符,这样就具备了同步的修饰功能,

编译运行也没有任何问题。改进后的方式比较简单,这个称之为同步函数。这是同步的第二种表现形式,同步代码块是第一种。

同步函数是第二种,都能实现同步的效果,解决线程的安全问题。

由于同步函数的出现,obj对象就没有用了,将其注释掉。原先同步代码块中,对象是锁,现在同步函数中,锁在哪儿?

package test;
/*
 * 需求:储户,两个,每个人都到银行存钱存100,共存三次
 */
class Bank{
	private int summomey;
	//Object obj = new Object();
	//加synchronized 修饰符
	public synchronized void add(int sum) //同步局函数
	{	//这样是一种方法
		//synchronized (obj) 
		//{
			summomey +=sum;
			System.out.println(summomey);
		//}
		
	}
}

class Cus implements Runnable
{	
	Bank b = new Bank();

	public void run()
	{	//Bank b = new Bank();//放在这就造成两个银行了
		//
		for(int i = 0;i<3;i++)
		{
			b.add(100);
		}
	}
	
}
public class BankDemo {

	public static void main(String[] args) 
	{
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		
		t1.start();
		t2.start();
	}

}

猜你喜欢

转载自blog.csdn.net/fighting_future/article/details/80300156