java多线程

  • 线程互斥

并发线程从账户取款示例

package myThread;

class Account {
	double balance;

	public Account(double money) {
		balance = money;
		System.out.println("Totle Money: " + balance);
	}
}

public class AccountThread extends Thread {
	Account account;
	int delay;

	public AccountThread(Account account, int delay) {
		this.account = account;
		this.delay = delay;
	}

	public void run() {
		if (account.balance >= 100) {
			try {
				sleep(delay);
				account.balance = account.balance - 100;
				System.out.println("withdraw  100 successful!");
			} catch (InterruptedException e) {
			}
		} else
			System.out.println("withdraw failed!");
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Account account = new Account(100);
		AccountThread acountThread1 = new AccountThread(account, 1000);
		AccountThread acountThread2 = new AccountThread(account, 0);
		acountThread1.start();
		acountThread2.start();
	}

}

执行以上代码得到结果

 

  • 该结果非常奇怪,因为尽管账面上只有100元,但是两个取钱线程都取得了100元钱,也就是总共得到了200元钱。出错的原因在哪里呢?
  • 由于线程1在判断满足取钱的条件后,被线程2打断,还没有来得及修改余额。因此线程2也满足取钱的条件,并完成了取钱动作。从而使共享数据balance的完整性被破坏。

  • 互斥对象

     上面的问题,并不是新问题,其实在并发程序设计中已经被研究并得到了解决。我们首先回忆两个概念。在并发程序设计中,对多线程共享的资源或数据成为临界资源,而把每个线(进)程中访问临界资源的那一段代码段成为临界代码段。通过为临界代码段设置信号灯,就可以保证资源的完整性,从而安全地访问共享资源

      为了实现这种机制,Java语言提供以下两方面的支持:
1 为每个对象设置了一个“互斥锁”标记。该标记保证在每一个时刻,只能有一个线程拥有该互斥锁,其它线程如果需要获得该互斥锁,必须等待当前拥有该锁的线程将其释放。该对象成为互斥对象。因此,java中的每一个对象都是互斥对象.
2 为了配合使用对象的互斥锁,Java语言提供了保留字synchronized.其基本用法如下:

synchronized(互斥对象){
    //临界代码段
}


使用互斥对象的并发线程示例

package myThread;

class Account {
	double balance;

	public Account(double money) {
		balance = money;
		System.out.println("Totle Money: " + balance);
	}
}

public class AccountThread extends Thread {
	Account account;
	int delay;

	public AccountThread(Account account, int delay) {
		this.account = account;
		this.delay = delay;
	}

	public void run() {
		synchronized (account) {
			if (account.balance >= 100) {
				try {
					sleep(delay);
					account.balance = account.balance - 100;
					System.out.println("withdraw  100 successful!");
				} catch (InterruptedException e) {
				}
			} else
				System.out.println("withdraw failed!");
		}
		
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Account account = new Account(100);
		AccountThread acountThread1 = new AccountThread(account, 1000);
		AccountThread acountThread2 = new AccountThread(account, 0);
		acountThread1.start();
		acountThread2.start();
	}
}

执行以上代码得到结果



  • 线程同步

在前面我们研究了共享资源的访问问题。在实际应用中,多个线程之间不仅需要互斥机制来保证对共享数据的完整性,而且有时需要多个线程之间互相协作,按照某种既定的步骤来共同完成任务。一个典型的应用是称之为生产-消费者模型。该模型可抽象为如下图。其约束条件为:
1)  生产者负责产品,并将其保存到仓库中;
2)   消费者从仓库中取得产品。
3) 由于库房容量有限,因此只有当库房还有空间时,生产者才可以将产品加入库房;否则只能等待。
只有库房中存在满足数量的产品时,消费者才能取走产品,否则只能等待。

实际应用中的许多例子都可以归结为该模型。如在操作系统中的打印机调度问题,库房的管理问题等。为了研究该问题,我们仍然以前面的存款与取款问题作为例子,假设存在一个账户对象(仓库)及两个线程:存款线程(生产者)和取款线程(消费者),并对其进行如下的限制;
1 只有当账户上的余额balance=0时,存款线程才可以存进100元;否则只能等待;

2 只有当账户上的余额balance=100时,取款线程才可以取走100元;否则只能等待。

根据生产-消费者模型,应该得到一个运行实例,存款100、取款100、存款100...很明显使用前面的synchronized关键字已经无法满足该实例线程同步实例。所以提供两个方法一个是wait(),一个notify()。

wait()方法的语义是:当一个线程执行了该方法,则该线程进入阻塞状态,同时让出同步对象的互斥锁,并自动进入互斥对象的等待队列。
notify()方法的语义是: 当一个线程执行了该方法,则拥有该方法的互斥对象的等待队列中的第一个线程被唤醒,同时自动获得该互斥对象的互斥锁,并进入就绪状态等待调度。
利用这两个方法,我们对上面的程序修改如下:

wait(),notify()使用实例

package myThread;

class Account1 {
	double balance;

	public Account1() {
		balance = 0;
		System.out.println("Totle Money: " + balance);
	}
	
	public synchronized void withdraw(double money) {
		if (balance==0) 
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		balance=balance-money;
		System.out.println("Totle:"+balance+"  withdraw 100 success");
		notify();
	}
	
	public synchronized void deposite(double money) {
		if (balance!=0)
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		balance=balance+money;
		System.out.println("Totle:"+balance+"  deposite 100 success");
		notify();
	}
	
}
//取钱线程
class WithdrawThread extends Thread{
	Account1 account1;
	public WithdrawThread(Account1 account1) {
		this.account1=account1;
	}
	@Override
	public void run() {
	for (int i=0;i<5;i++) {
		account1.withdraw(100);
	}
	}
}
//存钱线程

class DepositeThread extends Thread{
	Account1 account1;
	public DepositeThread(Account1 account1) {
		this.account1=account1;
	}
	@Override
	public void run() {
	for (int i=0;i<5;i++) {
		account1.deposite(100);
	}
	}
}

//测试
public class TestProCon {
 public static void main (String args[]) {
	 Account1 account1=new Account1();
	 WithdrawThread withdrawThread=new WithdrawThread(account1);
	 DepositeThread depositeThread=new DepositeThread(account1);
	 withdrawThread.start();
	 depositeThread.start();
	 }
}

执行以上代码


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值