zoukankan      html  css  js  c++  java
  • 多线程的同步解决方案

    一、线程同步

    线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多,临界区对象就是其中一种。

    在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。

    二、多线程同步解决方案

    2.1 同步代码块:

      使用 synchronized() 对需要完整执行的语句进行“包裹”,synchronized(Obj obj) 构造方法里是可以传入任何类的对象,

      但是既然是监听器就传一个唯一的对象来保证“锁”的唯一性,因此一般使用共享资源的对象来作为 obj 传入 synchronized(Obj obj) 里:

      只需要锁 Account 类中的存钱取钱方法就行了:

    package com.test.threadDemo2;
    
    /**
     * 银行账户
     * @author Administrator
     *
     */
    public class Acount {
        private int count=0;
        
        /**
         * 存钱
         * @param money
         */
        public void addAcount(String name,int money) {
            synchronized(this) {
                // 存钱
                count += money;
                System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
                SelectAcount(name);
            }
        }
        
        /**
         * 取钱
         * @param money
         */
        public void subAcount(String name,int money) {
            synchronized(this) {
                // 先判断账户现在的余额是否够取钱金额
                if(count-money < 0){  
                    System.out.println("账户余额不足!"); 
                    return;  
                } 
                // 取钱
                count -= money;
                System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
                SelectAcount(name);
            }
        }
        
        /**
         * 查询余额
         */
        public void SelectAcount(String name) {
            System.out.println(name+"...余额:"+count);
        }
    }

    2.2 同步方法

      或者在方法的申明里申明 synchronized 即可:

    package com.test.threadDemo2;
    /**
     * 银行账户
     * @author Administrator
     *
     */
    public class Acount {
        private int count;
        
        /**
         * 存钱
         * @param money
         */
        public synchronized void addAcount(String name,int money) {
                // 存钱
                count += money;
                System.out.println(name+"...存入:"+money);
        }
        
        /**
         * 取钱
         * @param money
         */
        public synchronized void subAcount(String name,int money) {
                // 先判断账户现在的余额是否够取钱金额
                if(count-money < 0){  
                    System.out.println("账户余额不足!");  
                    return;  
                } 
                // 取钱
                count -= money;
                System.out.println(name+"...取出:"+money);
        }
        
        /**
         * 查询余额
         */
        public void SelectAcount(String name) {
            System.out.println(name+"...余额:"+count);
        }
    }

    2.3 使用同步锁:

    account 类创建私有的 ReetrantLock 对象,调用 lock() 方法,同步执行体执行完毕之后,需要用 unlock() 释放锁。

    package com.test.threadDemo2;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 银行账户
     * @author Administrator
     *
     */
    public class Acount {
        private int count;
        private ReentrantLock lock = new ReentrantLock();
        
        /**
         * 存钱
         * @param money
         */
        public void addAcount(String name,int money) {
            lock.lock();
            try{
                // 存钱
                count += money;
                System.out.println(name+"...存入:"+money);
            }finally {
                lock.unlock();
            }
        }
        
        /**
         * 取钱
         * @param money
         */
        public void subAcount(String name,int money) {
            lock.lock();
            try{
                // 先判断账户现在的余额是否够取钱金额
                if(count-money < 0){  
                    System.out.println("账户余额不足!");  
                    return;  
                } 
                // 取钱
                count -= money;
                System.out.println(name+"...取出:"+money);
            }finally {
                lock.unlock();
            }
        }
        
        /**
         * 查询余额
         */
        public void SelectAcount(String name) {
            System.out.println(name+"...余额:"+count);
        }
    }

    三、线程通信

    在共享资源中增加镖旗,当镖旗为真的时候才可以存钱,存完了就把镖旗设置成假,当取款的时候发现镖旗为假的时候,可以取款,取完款就把镖旗设置为真。

    只需修改 Account 类 和 测试类 即可:

    package com.test.threadDemo2;
    
    /**
     * 银行账户
     * @author Administrator
     *
     */
    public class Acount {
        private boolean flag=false;    // 默认flag 为false,要求必须先存款再取款
        private int count=0;
        
        /**
         * 存钱
         * @param money
         */
        public void addAcount(String name,int money) {
            synchronized(this) {
                // flag 为false 表示可以存款,否则不可以存款
                if(flag) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }else {
                    // 存钱
                    count += money;
                    System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
                    SelectAcount(name);
                    flag = true;
                    this.notifyAll();
                }
            }
        }
        
        /**
         * 取钱
         * @param money
         */
        public void subAcount(String name,int money) {
            synchronized(this) {
                if(!flag) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                // 先判断账户现在的余额是否够取钱金额
                if(count-money < 0){  
                    System.out.println("账户余额不足!"); 
                    return;  
                } 
                    // 取钱
                    count -= money;
                    System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
                    SelectAcount(name);
                    flag = false;
                    this.notifyAll();
                }
            }
        }
        
        /**
         * 查询余额
         */
        public void SelectAcount(String name) {
            System.out.println(name+"...余额:"+count);
        }
    }
    package com.test.threadDemo2;
    
    public class ThreadDemo2 {
        public static void main(String[] args) {
            
            // 开个银行帐号
            Acount acount = new Acount();
            
            // 开银行帐号之后银行给张银行卡
            Card card1 = new Card("card1",acount);
            Card card2 = new Card("card2",acount);
            Card card3 = new Card("card3",acount);
            
            // 开银行帐号之后银行给张存折
            Paper paper1 = new Paper("paper1",acount);
            Paper paper2 = new Paper("paper2",acount);
            
            // 创建三个银行卡
            Thread thread1 = new Thread(card1,"card1");
            Thread thread2 = new Thread(card2,"card2");
            Thread thread3 = new Thread(card3,"card3");
            // 创建两个存折
            Thread thread4 = new Thread(paper1,"paper1");
            Thread thread5 = new Thread(paper2,"paper2");
            
            thread1.start();
            thread2.start();
            thread3.start();
            
            thread4.start();
            thread5.start();
        }
    }

     使用同步锁也可以达到相同的目的:

    package com.test.threadDemo2;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 银行账户
     * @author Administrator
     *
     */
    public class Acount2 {
        private boolean flag=false;    // 默认flag 为false,要求必须先存款再取款
        private int count=0;
        private final ReentrantLock lock = new ReentrantLock();
        private final Condition condition = lock.newCondition();
        
        /**
         * 存钱
         * @param money
         */
        public void addAcount(String name,int money) {
            lock.lock();
            try {
                // flag 为false 表示可以存款,否则不可以存款
                if(flag) {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }else {
                    // 存钱
                    count += money;
                    System.out.println(name+"...存入:"+money+"..."+Thread.currentThread().getName());
                    SelectAcount(name);
                    flag = true;
                    condition.signalAll();
                }
            }finally {
                lock.unlock();
            }
        }
        
        /**
         * 取钱
         * @param money
         */
        public void subAcount(String name,int money) {
            lock.lock();
            try {
                if(!flag) {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                // 先判断账户现在的余额是否够取钱金额
                if(count-money < 0){  
                    System.out.println("账户余额不足!"); 
                    return;  
                } 
                    // 取钱
                    count -= money;
                    System.out.println(name+"...取出:"+money+"..."+Thread.currentThread().getName());
                    SelectAcount(name);
                    flag = false;
                    condition.signalAll();
                }
            }finally {
                lock.unlock();
            }
        }
        
        /**
         * 查询余额
         */
        public void SelectAcount(String name) {
            System.out.println(name+"...余额:"+count);
        }
    }
  • 相关阅读:
    30天内自动登录
    本地保存cookie
    thymeleaf 基本表达式
    适配器(adapter)与fragment之间、fragment与activity之间的通信问题
    String/Stringbuilder/StringBuffer
    if else与switch for与foreach
    iOS App 签名的原理
    iOS category内部实现原理
    iOS中的事件的产生和传递
    RunLoop
  • 原文地址:https://www.cnblogs.com/windpoplar/p/11828083.html
Copyright © 2011-2022 走看看