zoukankan      html  css  js  c++  java
  • 线程同步(一)

    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
            {
            }
     
        }
    }
  • 相关阅读:
    .net winForm 实现类似qq 弹出新闻
    创业11年,我填过的5个大坑!(转)
    java中基本类型封装对象所占内存的大小(转)
    Java中如何创建进程(转)
    javac
    深入剖析Java中的装箱和拆箱(转)
    敏捷开发流程总结
    解决ccSvcHst.exe CPU占用超50%的问题,及其缘由
    JSP/ Servlet常见的中文乱码原因
    黑马程序猿-面向对象-多态
  • 原文地址:https://www.cnblogs.com/luhouxiang/p/2287633.html
Copyright © 2011-2022 走看看