利用synchronized实现多线程同步

        多线程编程带来便利性的同时,也给我们的编程带来了难度,因为多线程的执行具有随机性,当多个线程对共享资源操作时,就很容易引发问题。

      下面模拟了一个取钱的线程,当两个取钱的线程对同一个账户进行操作时,我们就会发现异常。

     下面是代码:

     

public class TestSyn {
   
	public static void main(String[] args) {
		 Account a =new Account(800);
		 WithDraw w1 = new WithDraw("取现1",a);
		 w1.start();
		 WithDraw w2 = new WithDraw("取现2",a);
		 w2.start();
	}
}
class WithDraw extends Thread
{
	Account a;
	
	public WithDraw(String name,Account a) {
		super(name);
		this.a = a;
	}

	public void run()
	{
	    a.withdraw(800)	;
	}
}
//定义一个账户类,用来模拟取现
class Account{
	int blance;

	public Account(int blance) {
		super();
		this.blance = blance;
	}

	public int getBlance() {
		return blance;
	}

	public void setBlance(int blance) {
		this.blance = blance;
	}
	//定义取款操作
	public void withdraw(int money)
	{
		if(money<=blance)
		{
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			setBlance(blance-money);
			System.out.println("取出"+money+"人民币");
		}
		else
		{
			System.out.println("余额不足,请确认");
		}
	}
}


     上面的账户余额只有800快,但是我们运行程序的时候,却发现他能连续两次取出800快,这就是多线程造成的读脏数据。为什么会出现这种情况呢?

     我们分析一下代码:当线程w1和w2执行的时候,我们假设w1先获得了cpu的执行权限,它调用run方法,执行取钱操作,执行if(money<=blance)语句,这时候money是800,账户a的余额也是800,判断条件成立,进入if里面的代码块。

     这时候Thread.sleep(1000)被调用,w1放弃cpu使用权,w2获得cpu使用权,执行run方法。它同样判断if(money<=blance)是否成立,因为之前w1线程还没有调用setBlance函数更新账户余额,所以w2线程也进入了if里面的代码块。

     所以w1线程和w2线程都进行了取钱操作,就造成了余额只有800块,但是我们却取出了1600块钱,这种悲剧。

    那么怎么解决这个问题呢,分析上面的代码,我们可以发现就是if(条件)判断成立后,进入其执行代码块时,我们最后连续执行完代码块(也就是得赶紧更新余额,或者说进入if代码块后,我们应该禁止别人进入if执行的代码块中了。

     java中给我们提供了synchronized关键字来解决上面的问题。

    1:利用同步代码块

     

public void withdraw(int money)
	{
		synchronized (this) {
			if(money<=blance)
			{
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				setBlance(blance-money);
				System.out.println("取出"+money+"人民币");
			}
			else
			{
				System.out.println("余额不足,请确认");
			}
		}
	}



上面使用的是同步代码块,synchronized后面跟的是锁对象,本程序中使用this对象作为锁对象,因为某一时刻只能有一个线程获得锁对象,所以当某个线程在取钱半途时间片到了,另外一个线程得到了执行机会,但是因为他没有获得锁对象,所以他还是不能进行取现操作,上面的代码是靠牺牲了时间来保证了多线程访问共享资源的准确性。

      2:我们还可以定义同步方法来控制线程的同步,同步方法默认使用this对象作为锁对象,下面是代码

     

public synchronized  void withdraw(int money)
	{
		
			if(money<=blance)
			{
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				setBlance(blance-money);
				System.out.println("取出"+money+"人民币");
			}
			else
			{
				System.out.println("余额不足,请确认");
			}
	}


你可能感兴趣的:(java基础)