线程同步(一)

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
        {
        }
 
    }
}

你可能感兴趣的:(线程同步)