• Java并发编程


    转自:http://www.verejava.com/?id=17236703718463

    锁Lock取款机场景实例

    Lock是一个接口,ReentrantLock是它的实现类,下面通过“取款机案例”来剖析它的4个常用方法。

    1.爸爸妈妈同时在ATM上登录取款(不加任何锁)

    public class Bank {
        private static double money = 10000;
    
        public void login(Thread currentUserThread) {
            System.out.println(Thread.currentThread().getName() + "  登录进入银行" + "  当前银行余额  : " + money);
    
        }
    
        public void logout() {
            System.out.println(Thread.currentThread().getName() + "  退出银行");
        }
    
        public double withdraw(double withdrawMoney) {
            if (this.money < withdrawMoney) {
                System.out.println(Thread.currentThread().getName() + " 当前银行余额  : " + this.money + " 余额不够");
                return 0;
            }
            this.money -= withdrawMoney;
            System.out.println(Thread.currentThread().getName() + "  取款  : " + withdrawMoney + "  当前银行余额  : " + this.money);
            return withdrawMoney;
        }
    }
    public class TestLock {
    
        public static void main(String[] args) {
            final Bank bank = new Bank();
    
            //启动爸爸线程
            Thread fatherThread = new Thread("爸爸") {
                public void run() {
                    try {
                        //爸爸登录
                        bank.login(Thread.currentThread());
                        //过2秒取10000
                        Thread.sleep(2000);
                        bank.withdraw(10000);
    
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            fatherThread.start();
    
            // 启动妈妈线程
            Thread motherThread = new Thread("妈妈") {
                public void run() {
                    try {
                        //妈妈登录
                        bank.login(Thread.currentThread());
                        //过5秒取 10000
                        Thread.sleep(5000);
                        bank.withdraw(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            motherThread.start();
        }
    }

    结果:

    妈妈  登录进入银行  当前银行余额  : 10000.0
    爸爸  登录进入银行  当前银行余额  : 10000.0
    爸爸  取款  : 10000.0  当前银行余额  : 0.0
    妈妈 当前银行余额  : 0.0 余额不够

    妈妈登录显示银行余额为10000,但是当她取钱时却显示“余额不足”,产生了数据不一致。

    2.同一时刻,爸爸或妈妈线程只能有一个能够登录银行取款(获取lock()),另外一个线程需要等待, 直到unlock()释放锁

    public class Bank {
        private static double money = 10000;
        private Lock lock = new ReentrantLock();
    
        public void login(Thread currentUserThread) {
            lock.lock();//登录加锁
            System.out.println(Thread.currentThread().getName() + "  登录进入银行" + "  当前银行余额  : " + money);
    
        }
    
        public void logout() {
            lock.unlock();//退出释放锁
            System.out.println(Thread.currentThread().getName() + "  退出银行");
        }
    
        public double withdraw(double withdrawMoney) {
            if (this.money < withdrawMoney) {
                System.out.println(Thread.currentThread().getName() + " 当前银行余额  : " + this.money + " 余额不够");
                return 0;
            }
            this.money -= withdrawMoney;
            System.out.println(Thread.currentThread().getName() + "  取款  : " + withdrawMoney + "  当前银行余额  : " + this.money);
            return withdrawMoney;
        }
    }
    public class TestLock {
    
        public static void main(String[] args) {
            final Bank bank = new Bank();
    
            //启动爸爸线程
            Thread fatherThread = new Thread("爸爸") {
                public void run() {
                    try {
                        //爸爸登录
                        bank.login(Thread.currentThread());
                        //过2秒取10000
                        Thread.sleep(2000);
                        bank.withdraw(10000);
                        //爸爸退出
                        bank.logout();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            fatherThread.start();
    
            // 启动妈妈线程
            Thread motherThread = new Thread("妈妈") {
                public void run() {
                    try {
                        //妈妈登录
                        bank.login(Thread.currentThread());
                        //过5秒取 10000
                        Thread.sleep(5000);
                        bank.withdraw(10000);
                        //妈妈退出
                        bank.logout();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            motherThread.start();
        }
    }

    结果:

    爸爸  登录进入银行  当前银行余额  : 10000.0
    爸爸  取款  : 10000.0  当前银行余额  : 0.0
    爸爸  退出银行
    妈妈  登录进入银行  当前银行余额  : 0.0
    妈妈 当前银行余额  : 0.0 余额不够
    妈妈  退出银行

    lock() 和unlock()成对出现,在login(Thread currentUserThread) 登录方法中调用 lock() ,在 logout()退出方法中调用了unlock()。
    也就是说,Lock类的锁机制允许在不同的方法中加锁和解锁,而synchronized关键字只能在同一个方法中加锁和解锁。

    3. 通过tryLock()判断是否可以获得锁, 能获得锁返回true,否则返回false

    public class Bank {
        private static double money = 10000;
        private Lock lock = new ReentrantLock();
    
        public void login(Thread currentUserThread) {
            //判断是否已经有线程登录
            if (!lock.tryLock()) {
                System.out.println(Thread.currentThread().getName() + " 有人已经登录进入银行  请稍等");
            } else {
                System.out.println(Thread.currentThread().getName() + "  登录进入银行" + "  当前银行余额: " + money);
            }
    
        }
    
        public void logout() {
            lock.unlock();//退出释放锁
            System.out.println(Thread.currentThread().getName() + "  退出银行");
        }
    
        public double withdraw(double withdrawMoney) {
            if (this.money < withdrawMoney) {
                System.out.println(Thread.currentThread().getName() + " 当前银行余额: " + this.money + " 余额不够");
                return 0;
            }
            this.money -= withdrawMoney;
            System.out.println(Thread.currentThread().getName() + "  取款: " + withdrawMoney + "  当前银行余额: " + this.money);
            return withdrawMoney;
        }
    }
    public class TestLock {
    
        public static void main(String[] args) {
            final Bank bank = new Bank();
    
            //启动爸爸线程
            Thread fatherThread = new Thread("爸爸") {
                public void run() {
                    try {
                        //爸爸登录
                        bank.login(Thread.currentThread());
                        //过2秒取10000
                        Thread.sleep(2000);
                        bank.withdraw(10000);
                        //爸爸退出
                        bank.logout();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            fatherThread.start();
    
            // 启动妈妈线程
            Thread motherThread = new Thread("妈妈") {
                public void run() {
                    //妈妈登录
                    bank.login(Thread.currentThread());
                }
            };
            motherThread.start();
        }
    }

    结果:

    妈妈 有人已经登录进入银行  请稍等
    爸爸  登录进入银行  当前银行余额: 10000.0
    爸爸  取款: 10000.0  当前银行余额: 0.0
    爸爸  退出银行

    4. 通过tryLock(long time,TimeUnit timeUnit)设置一段时间后重新进入 

    public class Bank {
        private static double money = 10000;
        private Lock lock = new ReentrantLock();
    
        public void login(Thread currentUserThread) {
            //如果登录不成功,10秒后再重新尝试获得锁
            try {
                if (!lock.tryLock(10, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread().getName() + " 有人已经登录进入银行,请稍等");
                } else {
                    System.out.println(Thread.currentThread().getName() + "  登录进入银行" + "  当前银行余额: " + money);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
        public void logout() {
            lock.unlock();//退出释放锁
            System.out.println(Thread.currentThread().getName() + "  退出银行");
        }
    
        public double withdraw(double withdrawMoney) {
            if (this.money < withdrawMoney) {
                System.out.println(Thread.currentThread().getName() + " 当前银行余额: " + this.money + " 余额不够");
                return 0;
            }
            this.money -= withdrawMoney;
            System.out.println(Thread.currentThread().getName() + "  取款: " + withdrawMoney + "  当前银行余额: " + this.money);
            return withdrawMoney;
        }
    }
    public class TestLock {
    
        public static void main(String[] args) {
            final Bank bank = new Bank();
    
            //启动爸爸线程
            Thread fatherThread = new Thread("爸爸") {
                public void run() {
                    try {
                        //爸爸登录
                        bank.login(Thread.currentThread());
                        //过2秒取10000
                        Thread.sleep(2000);
                        bank.withdraw(10000);
                        //爸爸退出
                        bank.logout();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            fatherThread.start();
    
            // 启动妈妈线程
            Thread motherThread = new Thread("妈妈") {
                public void run() {
                    //妈妈登录
                    bank.login(Thread.currentThread());
                }
            };
            motherThread.start();
        }
    }

    结果妈妈10秒后重新尝试得到锁登录进入了银行 :

    爸爸  登录进入银行  当前银行余额: 10000.0
    爸爸  取款: 10000.0  当前银行余额: 0.0
    爸爸  退出银行
    妈妈  登录进入银行  当前银行余额: 0.0

    5.如果爸爸线程调用 lock() 获得锁以后,没有unlock()释放锁,妈妈线程将会一直等待产生死锁,即使调用 interrupt()中断也没有作用。 

    public class Bank {
        private static double money = 10000;
        private Lock lock = new ReentrantLock();
    
        public void login(Thread currentUserThread) {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + " 登录进入银行" + "  当前银行余额  : " + money);
        }
    
        public void logout() {
            lock.unlock();//退出释放锁
            System.out.println(Thread.currentThread().getName() + "  退出银行");
        }
    
        public double withdraw(double withdrawMoney) {
            if (this.money < withdrawMoney) {
                System.out.println(Thread.currentThread().getName() + " 当前银行余额: " + this.money + " 余额不够");
                return 0;
            }
            this.money -= withdrawMoney;
            System.out.println(Thread.currentThread().getName() + "  取款: " + withdrawMoney + "  当前银行余额: " + this.money);
            return withdrawMoney;
        }
    }
    public class TestLock {
    
        public static void main(String[] args) {
            final Bank bank = new Bank();
    
            //启动爸爸线程
            Thread fatherThread = new Thread("爸爸") {
                public void run() {
                    try {
                        //爸爸登录
                        bank.login(Thread.currentThread());
                        //过2秒取10000
                        Thread.sleep(2000);
                        bank.withdraw(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            fatherThread.start();
    
            // 启动妈妈线程
            Thread motherThread = new Thread("妈妈") {
                public void run() {
                    try {
                        Thread.sleep(1000);
                        bank.login(Thread.currentThread());
                    } catch (InterruptedException e) {
                        System.out.println(Thread.currentThread().getName()+"  登录超时被中断");
                    }
                }
            };
            motherThread.start();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            motherThread.interrupt();
        }
    }

    结果:

    妈妈线程一直等待爸爸线程释放锁,结果造成死锁。妈妈线程调用motherThread.interrupt(); 中断也不起做用。

    6. 在登录获得锁时,调用lockInterruptibly(),允许中断等待的线程,使motherThread.interrupt()可用。

    public class Bank {
        private static double money = 10000;
        private Lock lock = new ReentrantLock();
    
        public void login(Thread currentUserThread) throws InterruptedException {
            lock.lockInterruptibly();//运行等待线程被中断
            System.out.println(Thread.currentThread().getName() + " 登录进入银行" + "  当前银行余额  : " + money);
        }
    
        public void logout() {
            lock.unlock();//退出释放锁
            System.out.println(Thread.currentThread().getName() + "  退出银行");
        }
    
        public double withdraw(double withdrawMoney) {
            if (this.money < withdrawMoney) {
                System.out.println(Thread.currentThread().getName() + " 当前银行余额: " + this.money + " 余额不够");
                return 0;
            }
            this.money -= withdrawMoney;
            System.out.println(Thread.currentThread().getName() + "  取款: " + withdrawMoney + "  当前银行余额: " + this.money);
            return withdrawMoney;
        }
    }
    public class TestLock {
    
        public static void main(String[] args) {
            final Bank bank = new Bank();
    
            //启动爸爸线程
            Thread fatherThread = new Thread("爸爸") {
                public void run() {
                    try {
                        //爸爸登录
                        bank.login(Thread.currentThread());
                        //过2秒取10000
                        Thread.sleep(2000);
                        bank.withdraw(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            fatherThread.start();
    
            // 启动 妈妈 线程
            Thread motherThread = new Thread("妈妈") {
                public void run() {
                    try {
                        Thread.sleep(1000);
                        bank.login(Thread.currentThread());
                    } catch (InterruptedException e) {
                        System.out.println(Thread.currentThread().getName()+"  登录超时被中断");
                    }
                }
            };
            motherThread.start();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            motherThread.interrupt();
        }
    }

    结果:

    爸爸 登录进入银行  当前银行余额  : 10000.0
    妈妈  登录超时被中断
    爸爸  取款: 10000.0  当前银行余额: 0.0
  • 相关阅读:
    coredata
    去除XIB上UITextView光标垂直居中问题
    IOS 开发使用SVN
    CSS层叠样式表--找到标签
    简单的爬虫
    HTML
    13索引
    12事务
    12内建函数
    11存储过程
  • 原文地址:https://www.cnblogs.com/helios-fz/p/11217177.html
Copyright © 2020-2023  润新知