线程同步(一)
Java的线程同步机制比较少,大致分两种,
一、锁对象
示例代码:
myLock.lock();//a reentrantLock object
try
{
//critical section
}
final
{
//make sure the lock is unlocked even if an exception is thrown
myLock.unlock();
}
这一结构确保任何时刻只有一个线程进入临界区。一旦一个线程封锁了锁对象,其它任何线程都无法通过lock语句。当其它线程调用lock时,它们被阻塞,直到第一个线程释放锁对象。注意:
1)此锁对象是对象级的,锁的是对象实例,
2)此锁对象是可重入的,同一线程中可以lock一次,两次,甚至更多,在同一线程中,如果lock了两次,必须unlock两次,其它线程才能再次访问。
示例代码:银行转账的示例。代码生成2个账户,每个账户1000元,要求2个账户同时随机转账而不出错。
银行用Bank类表示,里面实现了转账功能和查询总账的功能,账户用accounts数组表示。
程序主要由两个工作线程完成,每个工作线程执行这样的动作:向另一个账户转账,然后打印出两个账户的总额。(假设转账是免费的)
示例代码(此代码必然造成总额出错):
/**
* 客户测试代码,测试两个线程同时转账最终出错。
* @author luhx
*
*/
public class BankTransferTest
{
public static void main( String[] args )
{
System.out.println( "BankTransferTest begin!" );
Bank b = new Bank();
//第一个转账线程
TransferRunnable r0 = new TransferRunnable(b, 0, 1);
Thread t0 = new Thread(r0);
t0.start();
//第二个转账线程
TransferRunnable r1 = new TransferRunnable(b, 1, 0);
Thread t1 = new Thread(r1);
t1.start();
System.out.println( "BankTransferTest end!");
}
}
/**
* 银行类,实现转账功能和查询总账的功能
* @author luhx
*
*/
public class Bank {
public int []accounts = {1000, 1000};
/**
* 转账
* @param from
* @param to
* @param money
*/
void transfermoney(int from, int to, int money)
{
if(accounts[from] < money)//
return ;
accounts[from] -= money;
accounts[to] += money;
System.out.println("from:" + from + "to:" + to + "money:" + money +"");
System.out.println("totalMoney: " + getTotalMoney() + "");
}
/**
* 查询总金额
* @return
*/
int getTotalMoney()
{
int sum = 0;
for(int a : accounts)
{
sum += a;
}
return sum;
}
}
/**
* 线程类,将转账动作绑定到工作线程中执行
* @author luhx
*
*/
public class TransferRunnable implements Runnable{
private Bank bank;
private int from;
private int to;
final private int DELAY = 100;
TransferRunnable(Bank bank, int from, int to)
{
this.bank = bank;
this.from = from;
this.to = to;
}
public void run()
{
while(true)
{
int toAccount = (int)(1000 * Math.random());
try {
bank.transfermoney(from, to, toAccount);
Thread.sleep(DELAY);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
经过分析,我们得知,由于两个线程同时对账户1和2进行存取,必然存在冲突,因此需要同步,同步的方法很简单,只需在动到两账户的函数内加上锁即可,重新修改Bank类,然后再运行,发现总额总是2000,不再产生错误。
/**
* 银行类,实现转账功能和查询总账的功能
* @author luhx
*
*/
public class Bank {
public int []accounts = {1000, 1000};
//银行锁
private Lock bankLock;
private Condition condition;
Bank()
{
bankLock = new ReentrantLock();
condition = bankLock.newCondition();
}
/**
* 转账
* @param from
* @param to
* @param money
*/
void transfermoney(int from, int to, int money) throws InterruptedException
{
bankLock.lock();
try{
if(accounts[from] < money)//如果钱不够,则等待
condition.await();
accounts[from] -= money;
accounts[to] += money;
System.out.println("from:" + from + "to:" + to + "money:" + money +"");
System.out.println("totalMoney: " + getTotalMoney() + "");
condition.signalAll();
}
finally
{
bankLock.unlock();
}
}
/**
* 查询总金额
* @return
*/
int getTotalMoney()
{
bankLock.lock();
try{
int sum = 0;
for(int a : accounts)
{
sum += a;
}
return sum;
}
finally
{
bankLock.unlock();
}
}
}
使用对象锁比较麻烦,需要比较多的额外代码,可以用synchronized进行简化,使用之重改Bank类后如下所示
/**
* 银行类,实现转账功能和查询总账的功能
* @author luhx
*
*/
public class Bank {
public int []accounts = {1000, 1000};
/**
* 转账
* @param from
* @param to
* @param money
*/
public synchronized void transfermoney(int from, int to, int money) throws InterruptedException
{
if(accounts[from] < money)//如果钱不够,则等待
wait();
accounts[from] -= money;
accounts[to] += money;
System.out.println("from:" + from + "to:" + to + "money:" + money +"");
System.out.println("totalMoney: " + getTotalMoney() + "");
notifyAll();
}
/**
* 查询总金额
* @return
*/
public synchronized int getTotalMoney()
{
try{
int sum = 0;
for(int a : accounts)
{
sum += a;
}
return sum;
}
finally
{
}
}
}